JIT
Grid - Untitled
Create a box on the screen filled with 50 different, random fruit emojis. The emojis must have physics (matter.js style logic, but written from scratch): they fall, bounce off each other, and stack up. The user can pick them up and throw them with the mouse. Add a 'Mix' button that spins the container like a washing machine.
<html> <head> <title>Fruit Physics</title> </head> <body> <style> body { margin: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; background: #f0f0f0; } #box { position: relative; width: 80vw; height: 80vh; max-width: 1200px; max-height: 800px; border: 2px solid black; overflow: hidden; background: white; } .emoji { position: absolute; font-size: 32px; width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; user-select: none; pointer-events: none; } #mix { margin-top: 10px; padding: 10px 20px; font-size: 18px; } @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(1080deg); } } #box.spinning { animation: spin 2s linear; } </style> <div id="box"></div> <button id="mix">Mix</button> <script> const fruits = ['🍎', '🍏', '🍐', '🍊', 'πŸ‹', '🍌', 'πŸ‰', 'πŸ‡', 'πŸ“', '🫐', '🍈', 'πŸ’', 'πŸ‘', 'πŸ₯­', '🍍', 'πŸ₯₯', 'πŸ₯', 'πŸ…', 'πŸ†', 'πŸ₯‘']; const num = 50; const gravity = 0.2; const elasticity = 0.7; const friction = 0.99; let particles = []; let box = document.getElementById('box'); let rect = box.getBoundingClientRect(); let width = rect.width; let height = rect.height; const r = 16; for (let i = 0; i < num; i++) { let emoji = fruits[Math.floor(Math.random() * fruits.length)]; let div = document.createElement('div'); div.className = 'emoji'; div.innerHTML = emoji; box.appendChild(div); let x = Math.random() * (width - 2 * r) + r; let y = Math.random() * 200 - 300; let vx = 0; let vy = 0; particles.push({ elem: div, x, y, vx, vy, r }); } let selected = null; let prevMouseX = 0; let prevMouseY = 0; let isMixing = false; box.addEventListener('mousedown', e => { if (isMixing) return; let mx = e.clientX - rect.left; let my = e.clientY - rect.top; for (let p of particles) { let dx = mx - p.x; let dy = my - p.y; if (dx * dx + dy * dy < p.r * p.r * 1.5) { selected = p; prevMouseX = mx; prevMouseY = my; break; } } }); document.addEventListener('mousemove', e => { if (selected) { rect = box.getBoundingClientRect(); let mx = e.clientX - rect.left; let my = e.clientY - rect.top; selected.x = mx; selected.y = my; selected.vx = mx - prevMouseX; selected.vy = my - prevMouseY; prevMouseX = mx; prevMouseY = my; } }); document.addEventListener('mouseup', () => { selected = null; }); function collide(p1, p2) { let dx = p2.x - p1.x; let dy = p2.y - p1.y; let dist = Math.sqrt(dx * dx + dy * dy); if (dist < p1.r + p2.r && dist > 0) { let overlap = (p1.r + p2.r - dist); let nx = dx / dist; let ny = dy / dist; p1.x -= nx * overlap / 2; p1.y -= ny * overlap / 2; p2.x += nx * overlap / 2; p2.y += ny * overlap / 2; let v1n = p1.vx * nx + p1.vy * ny; let v2n = p2.vx * nx + p2.vy * ny; let delta = (v2n - v1n) * elasticity; p1.vx += delta * nx; p1.vy += delta * ny; p2.vx -= delta * nx; p2.vy -= delta * ny; } } function updateParticle(p) { p.vx *= friction; p.vy *= friction; p.vy += gravity; p.x += p.vx; p.y += p.vy; if (p.x < p.r) { p.x = p.r; p.vx = -p.vx * elasticity; } if (p.x > width - p.r) { p.x = width - p.r; p.vx = -p.vx * elasticity; } if (p.y < p.r) { p.y = p.r; p.vy = -p.vy * elasticity; } if (p.y > height - p.r) { p.y = height - p.r; p.vy = -p.vy * elasticity; if (Math.abs(p.vy) < 0.5) p.vy = 0; if (Math.abs(p.vx) < 0.5) p.vx = 0; } } function animate() { rect = box.getBoundingClientRect(); width = rect.width; height = rect.height; if (isMixing) { requestAnimationFrame(animate); return; } for (let i = 0; i < particles.length; i++) { let p = particles[i]; if (p === selected) continue; updateParticle(p); for (let j = i + 1; j < particles.length; j++) { let q = particles[j]; if (q !== selected) collide(p, q); } } particles.forEach(p => { p.elem.style.left = `${p.x - p.r}px`; p.elem.style.top = `${p.y - p.r}px`; }); requestAnimationFrame(animate); } animate(); document.getElementById('mix').addEventListener('click', () => { if (isMixing) return; isMixing = true; box.classList.add('spinning'); box.addEventListener('animationend', () => { box.classList.remove('spinning'); particles.forEach(p => { p.x = Math.random() * (width - 2 * p.r) + p.r; p.y = Math.random() * 200 - 300; p.vx = (Math.random() - 0.5) * 10; p.vy = 0; }); isMixing = false; }, { once: true }); }); </script> </body> </html>*
Updated files
v1