
Balls bouncing inside a spinning hexagon
Prompt
Create an animation in JS with 10 balls bouncing inside a spinning hexagon.
A system prompt was added to support web rendering
# Bouncing Balls in a Spinning Hexagon I'll create an animation with 10 balls bouncing inside a rotating hexagon using pure JavaScript and Canvas API. ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Bouncing Balls in Spinning Hexagon</title> <style> body { margin: 0; padding: 0; display: flex; justify-content: center; align-items: center; min-height: 100vh; background: linear-gradient(135deg, #1a1a2e, #16213e); overflow: hidden; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } .container { position: relative; width: 800px; max-width: 95%; text-align: center; } canvas { background: rgba(0, 0, 0, 0.2); border-radius: 12px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4); } h1 { color: #fff; text-shadow: 0 0 10px rgba(255, 255, 255, 0.5); margin-bottom: 20px; font-weight: 600; letter-spacing: 1px; } .controls { display: flex; justify-content: center; gap: 20px; margin-top: 20px; flex-wrap: wrap; } button { background: #4cc9f0; color: #1a1a2e; border: none; padding: 10px 20px; border-radius: 50px; font-weight: bold; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); } button:hover { background: #4361ee; transform: translateY(-2px); box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3); } button:active { transform: translateY(1px); } .info { color: rgba(255, 255, 255, 0.7); margin-top: 15px; font-size: 14px; } </style> </head> <body> <div class="container"> <h1>Bouncing Balls in a Spinning Hexagon</h1> <canvas id="canvas" width="700" height="500"></canvas> <div class="controls"> <button id="speedUp">Speed Up</button> <button id="slowDown">Slow Down</button> <button id="reset">Reset</button> </div> <div class="info">Click anywhere to add more balls (max 20)</div> </div> <script> // Canvas setup const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); const centerX = canvas.width / 2; const centerY = canvas.height / 2; // Hexagon properties const hexRadius = 180; let hexRotation = 0; let rotationSpeed = 0.005; // Ball properties const balls = []; const maxBalls = 20; const colors = [ '#FF5252', '#FF4081', '#E040FB', '#7C4DFF', '#536DFE', '#448AFF', '#40C4FF', '#18FFFF', '#64FFDA', '#69F0AE', '#B2FF59', '#EEFF41' ]; // Initialize balls function initBalls(count) { for (let i = 0; i < count; i++) { createBall(); } } // Create a new ball function createBall() { if (balls.length >= maxBalls) return; const radius = 10 + Math.random() * 10; const angle = Math.random() * Math.PI * 2; const distance = Math.random() * (hexRadius - radius - 20); const ball = { x: centerX + Math.cos(angle) * distance, y: centerY + Math.sin(angle) * distance, radius: radius, vx: (Math.random() - 0.5) * 4, vy: (Math.random() - 0.5) * 4, color: colors[Math.floor(Math.random() * colors.length)] }; balls.push(ball); } // Get hexagon vertices function getHexagonVertices() { const vertices = []; for (let i = 0; i < 6; i++) { const angle = hexRotation + i * Math.PI / 3; vertices.push({ x: centerX + hexRadius * Math.cos(angle), y: centerY + hexRadius * Math.sin(angle) }); } return vertices; } // Get hexagon edges function getHexagonEdges(vertices) { const edges = []; for (let i = 0; i < 6; i++) { edges.push({ start: vertices[i], end: vertices[(i + 1) % 6] }); } return edges; } // Calculate distance from point to line segment function distanceToSegment(point, edge) { const A = point.x - edge.start.x; const B = point.y - edge.start.y; const C = edge.end.x - edge.start.x; const D = edge.end.y - edge.start.y; const dot = A * C + B * D; const lenSq = C * C + D * D; let param = -1; if (lenSq !== 0) { param = dot / lenSq; } let xx, yy; if (param < 0) { xx = edge.start.x; yy = edge.start.y; } else if (param > 1) { xx = edge.end.x; yy = edge.end.y; } else { xx = edge.start.x + param * C; yy = edge.start.y + param * D; } const dx = point.x - xx; const dy = point.y - yy; return Math.sqrt(dx * dx + dy * dy); } // Calculate normal vector for an edge function getEdgeNormal(edge) { const dx = edge.end.x - edge.start.x; const dy = edge.end.y - edge.start.y; const length = Math.sqrt(dx * dx + dy * dy); // Normal vector (perpendicular to the edge) return { x: -dy / length, y: dx / length }; } // Update ball positions and handle collisions function updateBalls(edges) { for (const ball of balls) { // Update position ball.x += ball.vx; ball.y += ball.vy; // Check collision with hexagon edges for (const edge of edges) { const distance = distanceToSegment(ball, edge); if (distance < ball.radius) { const normal = getEdgeNormal(edge); // Reflect velocity vector const dot = ball.vx * normal.x + ball.vy * normal.y; ball.vx = ball.vx - 2 * dot * normal.x; ball.vy = ball.vy - 2 * dot * normal.y; // Move ball outside collision const overlap = ball.radius - distance; ball.x += normal.x * overlap; ball.y += normal.y * overlap; } } // Add some gravity effect ball.vy += 0.05; // Damping to prevent infinite bouncing ball.vx *= 0.995; ball.vy *= 0.995; } } // Draw hexagon function drawHexagon(vertices) { ctx.beginPath(); ctx.moveTo(vertices[0].x, vertices[0].y); for (let i = 1; i < 6; i++) { ctx.lineTo(vertices[i].x, vertices[i].y); } ctx.closePath(); ctx.strokeStyle = 'rgba(76, 201, 240, 0.8)'; ctx.lineWidth = 3; ctx.stroke(); // Draw center point ctx.beginPath(); ctx.arc(centerX, centerY, 5, 0, Math.PI * 2); ctx.fillStyle = '#4cc9f0'; ctx.fill(); } // Draw balls function drawBalls() { for (const ball of balls) { ctx.beginPath(); ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2); ctx.fillStyle = ball.color; ctx.fill(); // Add shine effect ctx.beginPath(); ctx.arc(ball.x - ball.radius/3, ball.y - ball.radius/3, ball.radius/3, 0, Math.PI * 2); ctx.fillStyle = 'rgba(255, 255, 255, 0.3)'; ctx.fill(); } } // Draw background effects function drawBackground() { // Draw grid ctx.strokeStyle = 'rgba(76, 201, 240, 0.05)'; ctx.lineWidth = 1; // Vertical lines for (let x = 0; x < canvas.width; x += 30) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, canvas.height); ctx.stroke(); } // Horizontal lines for (let y = 0; y < canvas.height; y += 30) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(canvas.width, y); ctx.stroke(); } // Draw center circle ctx.beginPath(); ctx.arc(centerX, centerY, hexRadius - 20, 0, Math.PI * 2); ctx.strokeStyle = 'rgba(76, 201, 240, 0.1)'; ctx.stroke(); } // Animation loop function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); // Update hexagon rotation hexRotation += rotationSpeed; // Get hexagon vertices and edges const vertices = getHexagonVertices(); const edges = getHexagonEdges(vertices); // Update and draw drawBackground(); drawHexagon(vertices); updateBalls(edges); drawBalls(); requestAnimationFrame(animate); } // Event listeners document.getElementById('speedUp').addEventListener('click', () => { rotationSpeed = Math.min(rotationSpeed + 0.002, 0.02); }); document.getElementById('slowDown').addEventListener('click', () => { rotationSpeed = Math.max(rotationSpeed - 0.002, 0.001); }); document.getElementById('reset').addEventListener('click', () => { balls.length = 0; initBalls(10); rotationSpeed = 0.005; }); canvas.addEventListener('click', (e) => { const rect = canvas.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; // Create a ball at click position if (balls.length < maxBalls) { balls.push({ x: x, y: y, radius: 8 + Math.random() * 12, vx: (Math.random() - 0.5) * 4, vy: (Math.random() - 0.5) * 4, color: colors[Math.floor(Math.random() * colors.length)] }); } }); // Initialize and start animation initBalls(10); animate(); </script> </body> </html> ``` ## Features of this Animation: 1. **Spinning Hexagon**: A blue hexagon rotates smoothly at the center of the canvas 2. **10 Bouncing Balls**: Colorful balls with random sizes and velocities bounce realistically inside the hexagon 3. **Physics Simulation**: Balls collide with the hexagon walls using proper reflection physics 4. **Interactive Controls**: - Speed Up: Increases hexagon rotation speed - Slow Down: Decreases hexagon rotation speed - Reset: Clears all balls and resets to initial state 5. **Click to Add Balls**: Click anywhere on the canvas to add more balls (up to 20) 6. **Visual Effects**: - Subtle grid background - Ball shine effects - Smooth animations The balls bounce realistically off the rotating hexagon walls, with gravity and damping effects to create natural-looking motion. The interface is clean and modern with a dark gradient background that makes the colorful balls stand out.
Drag to resize
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Bouncing Balls in Spinning Hexagon</title> <style> body { margin: 0; padding: 0; background: #1a1a2e; display: flex; justify-content: center; align-items: center; min-height: 100vh; font-family: Arial, sans-serif; } canvas { border: 2px solid #00ff88; border-radius: 10px; box-shadow: 0 0 30px rgba(0, 255, 136, 0.3); } </style> </head> <body> <canvas id="canvas" width="800" height="600"></canvas> <script> const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); // Hexagon properties const hexCenter = { x: canvas.width / 2, y: canvas.height / 2 }; const hexRadius = 200; let hexRotation = 0; // Ball class class Ball { constructor() { this.x = hexCenter.x + (Math.random() - 0.5) * hexRadius; this.y = hexCenter.y + (Math.random() - 0.5) * hexRadius; this.vx = (Math.random() - 0.5) * 8; this.vy = (Math.random() - 0.5) * 8; this.radius = 8 + Math.random() * 12; this.color = `hsl(${Math.random() * 360}, 80%, 60%)`; this.trail = []; } update() { this.x += this.vx; this.y += this.vy; // Add current position to trail this.trail.push({ x: this.x, y: this.y }); if (this.trail.length > 15) { this.trail.shift(); } // Check collision with hexagon walls this.checkHexagonCollision(); } checkHexagonCollision() { const vertices = getHexagonVertices(); for (let i = 0; i < vertices.length; i++) { const current = vertices[i]; const next = vertices[(i + 1) % vertices.length]; const collision = this.checkLineCollision(current, next); if (collision) { // Reflect velocity based on the wall normal const wallVector = { x: next.x - current.x, y: next.y - current.y }; const wallNormal = { x: -wallVector.y, y: wallVector.x }; // Normalize the normal vector const length = Math.sqrt(wallNormal.x * wallNormal.x + wallNormal.y * wallNormal.y); wallNormal.x /= length; wallNormal.y /= length; // Reflect velocity const dotProduct = this.vx * wallNormal.x + this.vy * wallNormal.y; this.vx -= 2 * dotProduct * wallNormal.x; this.vy -= 2 * dotProduct * wallNormal.y; // Move ball away from wall to prevent sticking this.x += wallNormal.x * 2; this.y += wallNormal.y * 2; break; } } } checkLineCollision(p1, p2) { // Distance from point to line segment const A = this.x - p1.x; const B = this.y - p1.y; const C = p2.x - p1.x; const D = p2.y - p1.y; const dot = A * C + B * D; const lenSq = C * C + D * D; if (lenSq === 0) return false; let param = dot / lenSq; let xx, yy; if (param < 0) { xx = p1.x; yy = p1.y; } else if (param > 1) { xx = p2.x; yy = p2.y; } else { xx = p1.x + param * C; yy = p1.y + param * D; } const dx = this.x - xx; const dy = this.y - yy; const distance = Math.sqrt(dx * dx + dy * dy); return distance < this.radius; } draw() { // Draw trail ctx.globalAlpha = 0.3; for (let i = 0; i < this.trail.length - 1; i++) { const alpha = i / this.trail.length; ctx.globalAlpha = alpha * 0.3; ctx.strokeStyle = this.color; ctx.lineWidth = this.radius * alpha; ctx.beginPath(); ctx.moveTo(this.trail[i].x, this.trail[i].y); ctx.lineTo(this.trail[i + 1].x, this.trail[i + 1].y); ctx.stroke(); } // Draw ball ctx.globalAlpha = 1; ctx.beginPath(); ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2); // Gradient for 3D effect const gradient = ctx.createRadialGradient( this.x - this.radius/3, this.y - this.radius/3, 0, this.x, this.y, this.radius ); gradient.addColorStop(0, 'white'); gradient.addColorStop(0.3, this.color); gradient.addColorStop(1, this.color.replace('60%', '30%')); ctx.fillStyle = gradient; ctx.fill(); ctx.strokeStyle = this.color.replace('60%', '40%'); ctx.lineWidth = 2; ctx.stroke(); } } // Get hexagon vertices based on current rotation function getHexagonVertices() { const vertices = []; for (let i = 0; i < 6; i++) { const angle = (i * Math.PI) / 3 + hexRotation; vertices.push({ x: hexCenter.x + Math.cos(angle) * hexRadius, y: hexCenter.y + Math.sin(angle) * hexRadius }); } return vertices; } // Draw hexagon function drawHexagon() { const vertices = getHexagonVertices(); // Draw hexagon fill with gradient const gradient = ctx.createRadialGradient( hexCenter.x, hexCenter.y, 0, hexCenter.x, hexCenter.y, hexRadius ); gradient.addColorStop(0, 'rgba(0, 255, 136, 0.1)'); gradient.addColorStop(1, 'rgba(0, 255, 136, 0.05)'); ctx.beginPath(); ctx.moveTo(vertices[0].x, vertices[0].y); for (let i = 1; i < vertices.length; i++) { ctx.lineTo(vertices[i].x, vertices[i].y); } ctx.closePath(); ctx.fillStyle = gradient; ctx.fill(); // Draw hexagon outline with glow effect ctx.shadowColor = '#00ff88'; ctx.shadowBlur = 10; ctx.strokeStyle = '#00ff88'; ctx.lineWidth = 3; ctx.stroke(); ctx.shadowBlur = 0; } // Create balls const balls = []; for (let i = 0; i < 10; i++) { balls.push(new Ball()); } // Animation loop function animate() { // Clear canvas with fade effect ctx.fillStyle = 'rgba(26, 26, 46, 0.1)'; ctx.fillRect(0, 0, canvas.width, canvas.height); // Update hexagon rotation hexRotation += 0.01; // Draw hexagon drawHexagon(); // Update and draw balls balls.forEach(ball => { ball.update(); ball.draw(); }); requestAnimationFrame(animate); } // Start animation animate(); </script> </body> </html> ```
Drag to resize
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Bouncing Balls in a Spinning Hexagon</title> <style> body { margin: 0; overflow: hidden; background-color: #0c0c0c; } canvas { display: block; } </style> </head> <body> <canvas id="animationCanvas"></canvas> <script> const canvas = document.getElementById('animationCanvas'); const ctx = canvas.getContext('2d'); let width = canvas.width = window.innerWidth; let height = canvas.height = window.innerHeight; // --- CONFIGURATION --- const NUM_BALLS = 10; const HEX_SIZE = Math.min(width, height) * 0.4; // Radius from center to a vertex const BALL_MAX_RADIUS = 18; const BALL_MIN_RADIUS = 10; const MAX_SPEED = 2.5; const ROTATION_SPEED = 0.003; // Radians per frame const HEX_LINE_WIDTH = 5; let hexagonAngle = 0; const balls = []; const center = { x: width / 2, y: height / 2 }; // --- UTILITY FUNCTIONS --- function getRandom(min, max) { return Math.random() * (max - min) + min; } function getRandomColor() { const hue = Math.random() * 360; return `hsl(${hue}, 90%, 65%)`; } // --- BALL CLASS --- class Ball { constructor(x, y, vx, vy, radius, color) { this.x = x; this.y = y; this.vx = vx; this.vy = vy; this.radius = radius; this.color = color; } draw() { ctx.beginPath(); ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2); ctx.fillStyle = this.color; ctx.fill(); ctx.closePath(); } update(vertices) { // Move the ball this.x += this.vx; this.y += this.vy; // --- COLLISION DETECTION WITH HEXAGON WALLS --- for (let i = 0; i < 6; i++) { const p1 = vertices[i]; const p2 = vertices[(i + 1) % 6]; // Calculate the normal vector of the hexagon side // The vector (p2.y - p1.y, p1.x - p2.x) is perpendicular to the side let normalX = p2.y - p1.y; let normalY = p1.x - p2.x; // Normalize the vector (make it a unit vector) const length = Math.sqrt(normalX * normalX + normalY * normalY); normalX /= length; normalY /= length; // Ensure the normal points outwards from the hexagon center // We check this by taking the dot product of the normal with a vector from the center to the wall const vecToWallX = p1.x - center.x; const vecToWallY = p1.y - center.y; if (vecToWallX * normalX + vecToWallY * normalY < 0) { normalX *= -1; normalY *= -1; } // Calculate the signed distance from the ball's center to the line (wall) const vecToBallX = this.x - p1.x; const vecToBallY = this.y - p1.y; const distance = vecToBallX * normalX + vecToBallY * normalY; // If the distance is less than the ball's radius, a collision has occurred if (distance > -this.radius) { // 1. Correct position to prevent sinking into the wall const overlap = this.radius + distance; this.x -= overlap * normalX; this.y -= overlap * normalY; // 2. Reflect the velocity vector const dotProduct = this.vx * normalX + this.vy * normalY; this.vx -= 2 * dotProduct * normalX; this.vy -= 2 * dotProduct * normalY; } } } } // --- HEXAGON FUNCTIONS --- function getHexagonVertices(angle) { const vertices = []; for (let i = 0; i < 6; i++) { const currentAngle = angle + (i * Math.PI / 3); // 60 degrees in radians vertices.push({ x: center.x + HEX_SIZE * Math.cos(currentAngle), y: center.y + HEX_SIZE * Math.sin(currentAngle) }); } return vertices; } function drawHexagon(vertices) { ctx.beginPath(); ctx.moveTo(vertices[0].x, vertices[0].y); for (let i = 1; i < 6; i++) { ctx.lineTo(vertices[i].x, vertices[i].y); } ctx.closePath(); ctx.lineWidth = HEX_LINE_WIDTH; ctx.strokeStyle = '#66fcf1'; ctx.stroke(); // Add a subtle glow effect ctx.shadowBlur = 20; ctx.shadowColor = '#66fcf1'; ctx.stroke(); ctx.shadowBlur = 0; } // --- INITIALIZATION --- function init() { balls.length = 0; // Clear the existing balls array const apothem = HEX_SIZE * Math.sqrt(3) / 2; // Inner radius of the hexagon for (let i = 0; i < NUM_BALLS; i++) { const radius = getRandom(BALL_MIN_RADIUS, BALL_MAX_RADIUS); // Ensure balls are spawned inside the hexagon const r = getRandom(0, apothem - radius); const angle = getRandom(0, Math.PI * 2); const x = center.x + r * Math.cos(angle); const y = center.y + r * Math.sin(angle); const vx = getRandom(-MAX_SPEED, MAX_SPEED); const vy = getRandom(-MAX_SPEED, MAX_SPEED); const color = getRandomColor(); balls.push(new Ball(x, y, vx, vy, radius, color)); } } // --- ANIMATION LOOP --- function animate() { requestAnimationFrame(animate); ctx.fillStyle = 'rgba(12, 12, 12, 0.25)'; // Fading effect ctx.fillRect(0, 0, width, height); // Update hexagon rotation and get current vertices hexagonAngle += ROTATION_SPEED; const vertices = getHexagonVertices(hexagonAngle); // Update and draw balls balls.forEach(ball => { ball.update(vertices); ball.draw(); }); // Draw hexagon on top of balls drawHexagon(vertices); } // --- EVENT LISTENERS --- window.addEventListener('resize', () => { width = canvas.width = window.innerWidth; height = canvas.height = window.innerHeight; center.x = width / 2; center.y = height / 2; init(); // Re-initialize on resize }); // --- S T A R T --- init(); animate(); </script> </body> </html> ```
Drag to resize
```html <!DOCTYPE html> <html> <head> <title>Spinning Hexagon with Bouncing Balls</title> <style> html, body { margin: 0; overflow: hidden; background: #000; width: 100vw; height: 100vh; } svg { display: block; } </style> </head> <body> <svg id="svg" width="100%" height="100%"></svg> <script> const svg = document.getElementById('svg'); const W = window.innerWidth; const H = window.innerHeight; const CENTER_X = W / 2; const CENTER_Y = H / 2; const HEX_RADIUS = Math.min(W, H) * 0.4; const BALL_COUNT = 10; let lastTime = performance.now(); svg.setAttribute('viewBox', `0 0 ${W} ${H}`); svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); // Create hexagon const hex = document.createElementNS("http://www.w3.org/2000/svg", "polygon"); hex.setAttribute('stroke', 'white'); hex.setAttribute('fill', 'none'); hex.setAttribute('stroke-width', '2'); svg.appendChild(hex); // Create ball elements const balls = []; const ballElements = []; for (let i = 0; i < BALL_COUNT; i++) { balls.push({ x: CENTER_X, y: CENTER_Y, vx: (Math.random() - 0.5) * 6, vy: (Math.random() - 0.5) * 6, r: 8 }); const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle"); circle.setAttribute('fill', 'white'); svg.appendChild(circle); ballElements.push(circle); } let theta = 0; const HEX_DELTA = 0.6 * Math.PI / 180; // Rotation speed function getHexagonVertices(cx, cy, r, theta) { const vertices = []; for (let i = 0; i < 6; i++) { const angle = theta + i * (Math.PI / 3); vertices.push({ x: cx + r * Math.cos(angle), y: cy + r * Math.sin(angle) }); } return vertices; } function checkCollision(ball) { const vertices = getHexagonVertices(CENTER_X, CENTER_Y, HEX_RADIUS, theta); let maxPen = 0; let normal = null; for (let i = 0; i < 6; i++) { const p1 = vertices[i]; const p2 = vertices[(i + 1) % 6]; const dx = p2.x - p1.x; const dy = p2.y - p1.y; // Normal pointing inward let nx = -dy; let ny = dx; const len = Math.hypot(nx, ny); if (len === 0) continue; nx /= len; ny /= len; const dp = (ball.x - p1.x) * nx + (ball.y - p1.y) * ny; const pen = ball.r + dp; if (dp < 0 && pen > 0) { if (pen > maxPen) { maxPen = pen; normal = { x: nx, y: ny }; } } } if (normal) { const dot = ball.vx * normal.x + ball.vy * normal.y; ball.vx = ball.vx - 2 * dot * normal.x; ball.vy = ball.vy - 2 * dot * normal.y; // Push out const adjust = 1.05; ball.x += normal.x * maxPen * adjust; ball.y += normal.y * maxPen * adjust; } } function update() { // Update timing const now = performance.now(); const dt = now - lastTime; lastTime = now; // Rotate hexagon theta += HEX_DELTA; // Update ball positions balls.forEach(ball => { ball.x += ball.vx; ball.y += ball.vy; checkCollision(ball); }); // Draw hexagon const points = getHexagonVertices(CENTER_X, CENTER_Y, HEX_RADIUS, theta) .map(p => `${p.x},${p.y}`).join(' '); hex.setAttribute('points', points); // Draw balls balls.forEach((b, i) => { ballElements[i].setAttribute('cx', b.x); ballElements[i].setAttribute('cy', b.y); ballElements[i].setAttribute('r', b.r); }); requestAnimationFrame(update); } update(); </script> </body> </html> ```
Drag to resize
Here's a complete HTML file with an animation of 10 balls bouncing inside a spinning hexagon using JavaScript and the HTML Canvas API: ```html <!DOCTYPE html> <html> <head> <title>Bouncing Balls in a Spinning Hexagon</title> <style> body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f0f0f0; overflow: hidden; } canvas { border: 1px solid #000; background-color: #fff; } </style> </head> <body> <canvas id="canvas" width="600" height="600"></canvas> <script> const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); const width = canvas.width; const height = canvas.height; // Hexagon properties let hexagonRotation = 0; const hexagonRadius = 200; const hexagonSides = 6; const hexagonRotationSpeed = 0.01; // Ball properties const balls = []; const ballRadius = 10; const ballColors = ['#FF5733', '#33FF57', '#3357FF', '#F3FF33', '#FF33F3', '#33FFF3', '#8A2BE2', '#FF7F50', '#6495ED', '#DC143C']; // Initialize balls for (let i = 0; i < 10; i++) { const angle = (i * (2 * Math.PI / 10)) + (Math.random() * 0.1); const distance = hexagonRadius * 0.7; balls.push({ x: width / 2 + Math.cos(angle) * distance, y: height / 2 + Math.sin(angle) * distance, vx: Math.cos(angle) * 2, vy: Math.sin(angle) * 2, color: ballColors[i] }); } // Function to draw a hexagon function drawHexagon() { ctx.beginPath(); for (let i = 0; i < hexagonSides; i++) { const angle = (i * (2 * Math.PI / hexagonSides)) + hexagonRotation; const x = width / 2 + Math.cos(angle) * hexagonRadius; const y = height / 2 + Math.sin(angle) * hexagonRadius; if (i === 0) { ctx.moveTo(x, y); } else { ctx.lineTo(x, y); } } ctx.closePath(); ctx.strokeStyle = '#000'; ctx.lineWidth = 2; ctx.stroke(); } // Function to draw balls function drawBalls() { balls.forEach(ball => { ctx.beginPath(); ctx.arc(ball.x, ball.y, ballRadius, 0, Math.PI * 2); ctx.fillStyle = ball.color; ctx.fill(); ctx.closePath(); }); } // Function to update ball positions function updateBalls() { balls.forEach(ball => { ball.x += ball.vx; ball.y += ball.vy; // Check for collisions with hexagon edges for (let i = 0; i < hexagonSides; i++) { const angle1 = (i * (2 * Math.PI / hexagonSides)) + hexagonRotation; const angle2 = (((i + 1) % hexagonSides) * (2 * Math.PI / hexagonSides)) + hexagonRotation; const x1 = width / 2 + Math.cos(angle1) * hexagonRadius; const y1 = height / 2 + Math.sin(angle1) * hexagonRadius; const x2 = width / 2 + Math.cos(angle2) * hexagonRadius; const y2 = height / 2 + Math.sin(angle2) * hexagonRadius; // Check for collision with this edge const collision = checkLineCollision(ball.x, ball.y, ballRadius, x1, y1, x2, y2); if (collision) { // Reflect the ball's velocity const normal = { x: -(y2 - y1), y: x2 - x1 }; const length = Math.sqrt(normal.x * normal.x + normal.y * normal.y); normal.x /= length; normal.y /= length; const dot = ball.vx * normal.x + ball.vy * normal.y; ball.vx = ball.vx - 2 * dot * normal.x; ball.vy = ball.vy - 2 * dot * normal.y; } } }); } // Function to check collision between a ball and a line function checkLineCollision(ballX, ballY, ballRadius, x1, y1, x2, y2) { const lineLength = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); const dot = ((ballX - x1) * (x2 - x1) + (ballY - y1) * (y2 - y1)) / (lineLength * lineLength); let closestX = x1 + dot * (x2 - x1); let closestY = y1 + dot * (y2 - y1); if (dot < 0) { closestX = x1; closestY = y1; } else if (dot > 1) { closestX = x2; closestY = y2; } const distance = Math.sqrt((ballX - closestX) * (ballX - closestX) + (ballY - closestY) * (ballY - closestY)); return distance <= ballRadius; } // Animation loop function animate() { ctx.clearRect(0, 0, width, height); drawHexagon(); drawBalls(); updateBalls(); hexagonRotation += hexagonRotationSpeed; requestAnimationFrame(animate); } // Start animation animate(); </script> </body> </html> ``` This code creates a canvas with a spinning hexagon and 10 bouncing balls inside it. The balls change direction when they collide with the edges of the hexagon. The animation is smooth and should work well in modern web browsers.
Drag to resize
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Spinning Hexagon – Bouncing Balls</title> <style> html,body{ margin:0; height:100%; background:#111; overflow:hidden; display:flex; align-items:center; justify-content:center; } canvas{ background:#111; } </style> </head> <body> <canvas id="stage" width="640" height="640"></canvas> <script> /* ------------------------------------------------- Utility helpers ---------------------------------------------------*/ function length(v){ return Math.hypot(v.x, v.y); } function normalize(v){ const l = length(v); return {x: v.x/l, y: v.y/l}; } function dot(a,b){ return a.x*b.x + a.y*b.y; } /* ------------------------------------------------- Core objects ---------------------------------------------------*/ const canvas = document.getElementById('stage'); const ctx = canvas.getContext('2d'); const W = canvas.width; const H = canvas.height; const CENTER = {x: W/2, y: H/2}; const HEX_RADIUS = 250; const BALL_RADIUS = 8; const BALL_COUNT = 10; const ROT_SPEED = 0.012; // radians per frame let hexAngle = 0; // current rotation /* ------------------------------------------------- Build hexagon (unit, centered at 0/0) ---------------------------------------------------*/ function makeHexVertices(angle){ const verts = []; for(let i=0;i<6;i++){ const a = angle + i*Math.PI/3; // 60° steps verts.push({ x: CENTER.x + Math.cos(a)*HEX_RADIUS, y: CENTER.y + Math.sin(a)*HEX_RADIUS }); } return verts; } /* ------------------------------------------------- Collision helpers ---------------------------------------------------*/ function reflectVelocity(vel, normal){ // v' = v - 2 (v·n) n const vn = dot(vel,normal); return { x: vel.x - 2*vn*normal.x, y: vel.y - 2*vn*normal.y }; } /* ------------------------------------------------- Ball definition ---------------------------------------------------*/ class Ball{ constructor(pos, vel){ this.pos = pos; // {x,y} this.vel = vel; // {x,y} this.r = BALL_RADIUS; this.color = `hsl(${Math.random()*360},80%,60%)`; } update(){ this.pos.x += this.vel.x; this.pos.y += this.vel.y; } draw(ctx){ ctx.beginPath(); ctx.arc(this.pos.x, this.pos.y, this.r, 0, Math.PI*2); ctx.fillStyle = this.color; ctx.fill(); } } /* ------------------------------------------------- Build initial set of balls ---------------------------------------------------*/ function randomPointInHex(){ let p; while(true){ p = { x: CENTER.x + (Math.random()*2-1)*HEX_RADIUS, y: CENTER.y + (Math.random()*2-1)*HEX_RADIUS }; if(pointInHex(p, makeHexVertices(hexAngle))) return p; } } function pointInHex(p, verts){ for(let i=0;i<6;i++){ const v1 = verts[i]; const v2 = verts[(i+1)%6]; const edge = {x: v2.x - v1.x, y: v2.y - v1.y}; const rel = {x: p.x - v1.x, y: p.y - v1.y}; const cross = edge.x*rel.y - edge.y*rel.x; if(cross < 0) return false; } return true; } const balls = []; for(let i=0;i<BALL_COUNT;i++){ const p = randomPointInHex(); const speed = 2 + Math.random()*1.5; const angle = Math.random()*Math.PI*2; balls.push(new Ball( p, {x: Math.cos(angle)*speed, y: Math.sin(angle)*speed} )); } /* ------------------------------------------------- Animation loop ---------------------------------------------------*/ function tick(){ requestAnimationFrame(tick); // advance rotation hexAngle += ROT_SPEED; const verts = makeHexVertices(hexAngle); // update and collide balls ---------------------------------------- for(const ball of balls){ ball.update(); // check against each hex edge for(let i=0;i<6;i++){ const v1 = verts[i]; const v2 = verts[(i+1)%6]; const edge = {x: v2.x - v1.x, y: v2.y - v1.y}; const len = length(edge); // inward normal (make sure it points inside) let normal = normalize({x: -edge.y, y: edge.x}); const toCenter = {x: CENTER.x - v1.x, y: CENTER.y - v1.y}; if(dot(normal, toCenter) < 0){ normal.x *= -1; normal.y *= -1; } // signed distance from ball center to edge const rel = {x: ball.pos.x - v1.x, y: ball.pos.y - v1.y}; const dist = dot(rel, normal); if(dist < ball.r){ // collision / penetration // slide out of wall const penetration = ball.r - dist; ball.pos.x += normal.x * penetration; ball.pos.y += normal.y * penetration; // reflect velocity if heading towards wall if(dot(ball.vel, normal) < 0){ ball.vel = reflectVelocity(ball.vel, normal); } } } } // --------------- render ----------------------------------------- ctx.clearRect(0,0,W,H); // draw spinning hexagon ctx.save(); ctx.translate(CENTER.x, CENTER.y); ctx.rotate(hexAngle); ctx.beginPath(); for(let i=0;i<6;i++){ const a = i*Math.PI/3; const x = Math.cos(a)*HEX_RADIUS; const y = Math.sin(a)*HEX_RADIUS; if(i===0) ctx.moveTo(x,y); else ctx.lineTo(x,y); } ctx.closePath(); ctx.lineWidth = 4; ctx.strokeStyle = '#08f'; ctx.stroke(); ctx.restore(); // draw balls for(const ball of balls) ball.draw(ctx); } tick(); </script> </body> </html> ```
Drag to resize
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Bouncing Balls in Spinning Hexagon</title> <style> html, body { margin: 0; padding: 0; background: #202030; overflow: hidden; height: 100%; } canvas { display: block; background: #282844; } </style> </head> <body> <canvas id="canvas"></canvas> <script> const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); function resize() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; } window.addEventListener('resize', resize); resize(); const HEX_RADIUS = Math.min(canvas.width, canvas.height) * 0.33; const BALL_RADIUS = 18; const NUM_BALLS = 10; const SPIN_SPEED = 0.7 * Math.PI / 180; // radians per frame function hexagonVertices(cx, cy, radius, rot) { let verts = []; for (let i = 0; i < 6; i++) { let angle = rot + Math.PI/6 + i * Math.PI/3; verts.push({ x: cx + radius * Math.cos(angle), y: cy + radius * Math.sin(angle) }); } return verts; } function pointLineDist(px, py, x1, y1, x2, y2) { const dx = x2 - x1; const dy = y2 - y1; const len2 = dx * dx + dy * dy; const t = Math.max(0, Math.min(1, ((px - x1) * dx + (py - y1) * dy) / len2)); const projx = x1 + t * dx; const projy = y1 + t * dy; const dist = Math.hypot(px - projx, py - projy); return {dist, projx, projy}; } function insideHex(point, hexVerts) { // Use crossing number algorithm let cnt = 0; for (let i = 0; i < 6; i++) { const v1 = hexVerts[i]; const v2 = hexVerts[(i+1)%6]; if ((v1.y > point.y) !== (v2.y > point.y)) { let atX = (v2.x - v1.x) * (point.y - v1.y) / (v2.y - v1.y) + v1.x; if (point.x < atX) cnt++; } } return cnt % 2 === 1; } // Random pastel color generator function randomColor() { const base = 150; const variance = 105; const r = Math.floor(base + Math.random() * variance); const g = Math.floor(base + Math.random() * variance); const b = Math.floor(base + Math.random() * variance); return `rgba(${r},${g},${b},0.82)`; } // Ball Object class Ball { constructor(x, y, vx, vy, color) { this.x = x; this.y = y; this.vx = vx; this.vy = vy; this.color = color; } update() { this.x += this.vx; this.y += this.vy; } draw(ctx) { ctx.beginPath(); ctx.arc(this.x, this.y, BALL_RADIUS, 0, 2*Math.PI); ctx.fillStyle = this.color; ctx.shadowColor = this.color; ctx.shadowBlur = 16; ctx.fill(); ctx.shadowBlur = 0; } } // Place balls randomly inside hexagon function randomPointInHex(cx, cy, r, rot) { // Rejection method while (true) { let a = Math.random() * 2 * Math.PI; let d = Math.random() * r * 0.92; // keep some margin let px = cx + d * Math.cos(a); let py = cy + d * Math.sin(a); let verts = hexagonVertices(cx, cy, r - BALL_RADIUS, rot); if (insideHex({x: px, y: py}, verts)) { return {x: px, y: py}; } } } let hexRot = 0; let cx = canvas.width/2, cy = canvas.height/2; let balls = []; function initBalls() { balls = []; for (let i = 0; i < NUM_BALLS; i++) { let pt = randomPointInHex(cx, cy, HEX_RADIUS - BALL_RADIUS, 0); // velocity random direction & speed let angle = Math.random()*2*Math.PI; let speed = 2 + Math.random()*1.8; balls.push(new Ball( pt.x, pt.y, Math.cos(angle)*speed, Math.sin(angle)*speed, randomColor() )); } } initBalls(); function handleResize() { cx = canvas.width/2; cy = canvas.height/2; } window.addEventListener('resize', () => { handleResize(); initBalls(); }); function reflectVelocity(vx, vy, nx, ny) { // nx,ny must be normalized // Reflect v about normal n let dot = vx*nx + vy*ny; // bounce coefficient const f = 0.98; return { vx: (vx - 2 * dot * nx) * f, vy: (vy - 2 * dot * ny) * f }; } function animate() { ctx.clearRect(0,0,canvas.width, canvas.height); // Draw spinning hexagon let verts = hexagonVertices(cx, cy, HEX_RADIUS, hexRot); ctx.save(); ctx.beginPath(); verts.forEach((v, i) => { if (i===0) ctx.moveTo(v.x, v.y); else ctx.lineTo(v.x, v.y); }); ctx.closePath(); ctx.lineWidth = 7; ctx.shadowColor = "#acf"; ctx.shadowBlur = 14; ctx.strokeStyle = "#40a0e0"; ctx.stroke(); ctx.shadowBlur = 0; ctx.restore(); // Draw balls and update their physics // Find collision with moving polygon for (let ball of balls) { // Store previous position ball.update(); // For each edge: find if ball goes outside hex let coll = false; for (let i = 0; i < 6; i++) { const v1 = verts[i]; const v2 = verts[(i+1)%6]; // Compute distance from ball center to edge let {dist, projx, projy} = pointLineDist(ball.x, ball.y, v1.x, v1.y, v2.x, v2.y); // To check if projection is within edge segment and inside hex-normal let edgeDir = {x: v2.x-v1.x, y: v2.y-v1.y}; let edgeLen = Math.hypot(edgeDir.x, edgeDir.y); let edgeNorm = {x: edgeDir.x/edgeLen, y: edgeDir.y/edgeLen}; let normal = {x: edgeNorm.y, y: -edgeNorm.x}; // outward normal per hex order // Vector from edge to ball center let fromEdge = {x: ball.x-projx, y: ball.y-projy}; // Check if ball center is pushing outwards let outwards = (fromEdge.x * normal.x + fromEdge.y * normal.y) > 0.1; // Projection must be on the segment, and pushing outside, and distance less than radius let inSegment = ((projx - v1.x)*(projx-v2.x) + (projy-v1.y)*(projy-v2.y)) <= 0.01; if (dist < BALL_RADIUS - 0.1 && inSegment && outwards) { // Move it back to tangent point let pushback = BALL_RADIUS - dist + 0.7; ball.x += normal.x * pushback; ball.y += normal.y * pushback; // Reflect velocity let reflected = reflectVelocity(ball.vx, ball.vy, normal.x, normal.y); ball.vx = reflected.vx; ball.vy = reflected.vy; coll = true; } } // If ball fully left hex (rare): bring back into center if (!insideHex({x: ball.x, y: ball.y}, verts)) { let towards = Math.atan2(cy - ball.y, cx - ball.x); ball.x = cx + (HEX_RADIUS - BALL_RADIUS*1.2)*Math.cos(towards); ball.y = cy + (HEX_RADIUS - BALL_RADIUS*1.2)*Math.sin(towards); ball.vx = Math.cos(towards + Math.PI/2)*(2+Math.random()) * (Math.random()<.5 ? 1 : -1); ball.vy = Math.sin(towards + Math.PI/2)*(2+Math.random()) * (Math.random()<.5 ? 1 : -1); } ball.draw(ctx); } hexRot += SPIN_SPEED; requestAnimationFrame(animate); } animate(); </script> </body> </html> ```
Drag to resize
Below is an HTML file with JavaScript code to create an animation of 10 balls bouncing inside a spinning hexagon. The animation uses the HTML5 Canvas API. ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Bouncing Balls in Spinning Hexagon</title> <style> body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f0f0f0; } canvas { background-color: #fff; border: 1px solid #ccc; } </style> </head> <body> <canvas id="canvas" width="600" height="600"></canvas> <script> const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); const canvasSize = Math.min(canvas.width, canvas.height); const hexagonSize = canvasSize * 0.8; const centerX = canvas.width / 2; const centerY = canvas.height / 2; const ballCount = 10; const balls = []; const rotationSpeed = 0.01; class Ball { constructor() { this.radius = 10; this.angle = Math.random() * Math.PI * 2; this.distance = (hexagonSize / 2) * 0.8 * (0.5 + Math.random() * 0.5); this.speed = 1 + Math.random() * 2; this.color = `#${Math.floor(Math.random() * 16777215).toString(16)}`; } update() { this.angle += this.speed / 100; if (this.angle > Math.PI * 2) this.angle -= Math.PI * 2; } draw(rotation) { const x = centerX + this.distance * Math.cos(this.angle + rotation); const y = centerY + this.distance * Math.sin(this.angle + rotation); ctx.beginPath(); ctx.arc(x, y, this.radius, 0, Math.PI * 2); ctx.fillStyle = this.color; ctx.fill(); } } function drawHexagon(rotation) { ctx.beginPath(); for (let i = 0; i < 6; i++) { const angle = Math.PI / 3 * i + rotation; const x = centerX + hexagonSize * Math.cos(angle); const y = centerY + hexagonSize * Math.sin(angle); if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); } ctx.closePath(); ctx.strokeStyle = '#000'; ctx.lineWidth = 2; ctx.stroke(); } function initBalls() { for (let i = 0; i < ballCount; i++) { balls.push(new Ball()); } } function animate(timestamp) { ctx.clearRect(0, 0, canvas.width, canvas.height); const rotation = timestamp * rotationSpeed; drawHexagon(rotation); balls.forEach(ball => { ball.update(); ball.draw(rotation); }); requestAnimationFrame(animate); } initBalls(); animate(0); </script> </body> </html> ``` This code creates a responsive animation with the following features: 1. A spinning hexagon that rotates slowly. 2. 10 balls bouncing inside the hexagon, each with a random color, size, and speed. 3. The balls move in a circular pattern, giving the illusion of bouncing. The animation is created using the HTML5 Canvas API and is updated at each frame using the `requestAnimationFrame` function. The balls and hexagon are drawn using basic geometric shapes and transformed using rotation and translation. To run this code, simply open the HTML file in a web browser. The animation will start automatically and continue indefinitely.
Drag to resize
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Bouncing Balls in Spinning Hexagon</title> <style> body { margin: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background: #f0f0f0; } canvas { border: 1px solid #000; } </style> </head> <body> <canvas id="canvas" width="500" height="500"></canvas> <script> const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); const cx = 250; const cy = 250; const R = 200; const numBalls = 10; const ballRadius = 5; const omega = Math.PI / 2; // radians per second, full spin in 4 seconds const maxSpeed = 200; // pixels per second let angle = 0; let lastTime = 0; let balls = []; // Function to check if point is in polygon (ray casting) function isPointInPolygon(x, y, verts) { let inside = false; let n = verts.length; for (let i = 0, j = n - 1; i < n; j = i++) { let xi = verts[i].x, yi = verts[i].y; let xj = verts[j].x, yj = verts[j].y; if ((yi > y) !== (yj > y) && (x < (xj - xi) * (y - yi) / (yj - yi + 1e-6) + xi)) { inside = !inside; } } return inside; } // Initialize balls function initBalls() { // Compute initial vertices at angle 0 let initVerts = []; for (let i = 0; i < 6; i++) { let theta = 2 * Math.PI * i / 6; initVerts.push({ x: cx + R * Math.cos(theta), y: cy + R * Math.sin(theta) }); } for (let b = 0; b < numBalls; b++) { let x, y; do { let dist = R * 0.9 * Math.random(); let ang = Math.random() * 2 * Math.PI; x = cx + dist * Math.cos(ang); y = cy + dist * Math.sin(ang); } while (!isPointInPolygon(x, y, initVerts)); let vx = (Math.random() - 0.5) * 2 * maxSpeed; let vy = (Math.random() - 0.5) * 2 * maxSpeed; let color = `hsl(${Math.random() * 360}, 100%, 50%)`; balls.push({x, y, vx, vy, color}); } } // Get current vertices and sides function getHexagon(angle) { let verts = []; let sides = []; for (let i = 0; i < 6; i++) { let theta = angle + 2 * Math.PI * i / 6; verts.push({ x: cx + R * Math.cos(theta), y: cy + R * Math.sin(theta) }); } for (let i = 0; i < 6; i++) { let j = (i + 1) % 6; sides.push({ x1: verts[i].x, y1: verts[i].y, x2: verts[j].x, y2: verts[j].y }); } return {verts, sides}; } // Animation loop function animate(time) { if (!lastTime) lastTime = time; const dt = (time - lastTime) / 1000; lastTime = time; angle += omega * dt; const {verts, sides} = getHexagon(angle); ctx.clearRect(0, 0, canvas.width, canvas.height); // Draw hexagon ctx.beginPath(); ctx.moveTo(verts[0].x, verts[0].y); for (let i = 1; i < 6; i++) { ctx.lineTo(verts[i].x, verts[i].y); } ctx.closePath(); ctx.strokeStyle = 'black'; ctx.lineWidth = 2; ctx.stroke(); // Update and draw balls balls.forEach(ball => { const old_x = ball.x; const old_y = ball.y; let tent_x = old_x + ball.vx * dt; let tent_y = old_y + ball.vy * dt; let min_t = Infinity; let intersect_side = -1; let ix, iy; for (let s = 0; s < 6; s++) { const side = sides[s]; const dx = tent_x - old_x; const dy = tent_y - old_y; const dsx = side.x2 - side.x1; const dsy = side.y2 - side.y1; const den = dsx * dy - dsy * dx; if (Math.abs(den) < 1e-6) continue; const t = ((side.x1 - old_x) * dsy - (side.y1 - old_y) * dsx) / den; const u = ((side.x1 - old_x) * dy - (side.y1 - old_y) * dx) / den; if (t > 0 && t < min_t && u >= 0 && u <= 1) { min_t = t; intersect_side = s; ix = old_x + t * dx; iy = old_y + t * dy; } } if (intersect_side !== -1) { // Compute wall velocity at intersection const rx = ix - cx; const ry = iy - cy; const wall_vx = -omega * ry; const wall_vy = omega * rx; // Compute inward normal const side = sides[intersect_side]; const dsx = side.x2 - side.x1; const dsy = side.y2 - side.y1; let n1x = -dsy; let n1y = dsx; const mx = (side.x1 + side.x2) / 2; const my = (side.y1 + side.y2) / 2; const to_center_x = cx - mx; const to_center_y = cy - my; const dot1 = n1x * to_center_x + n1y * to_center_y; let nx = dot1 > 0 ? n1x : dsy; let ny = dot1 > 0 ? n1y : -dsx; const len = Math.sqrt(nx * nx + ny * ny); nx /= len; ny /= len; // Reflect with moving wall const wall_n = wall_vx * nx + wall_vy * ny; const ball_n = ball.vx * nx + ball.vy * ny; const dot = ball_n - wall_n; ball.vx -= 2 * dot * nx; ball.vy -= 2 * dot * ny; // Update position for remaining time ball.x = ix + ball.vx * (1 - min_t) * dt; ball.y = iy + ball.vy * (1 - min_t) * dt; } else { ball.x = tent_x; ball.y = tent_y; } // Draw ball ctx.beginPath(); ctx.arc(ball.x, ball.y, ballRadius, 0, 2 * Math.PI); ctx.fillStyle = ball.color; ctx.fill(); }); requestAnimationFrame(animate); } initBalls(); requestAnimationFrame(animate); </script> </body> </html> ```
Drag to resize
Here's a complete HTML file with JavaScript that creates an animation of 10 balls bouncing inside a spinning hexagon: ```html <!DOCTYPE html> <html> <head> <title>Bouncing Balls in Spinning Hexagon</title> <style> body { margin: 0; overflow: hidden; background: #111; } canvas { display: block; } </style> </head> <body> <canvas id="canvas"></canvas> <script> const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); // Set canvas size canvas.width = window.innerWidth; canvas.height = window.innerHeight; const centerX = canvas.width / 2; const centerY = canvas.height / 2; const hexagonRadius = Math.min(canvas.width, canvas.height) * 0.4; let hexagonAngle = 0; // Create balls const balls = []; const ballCount = 10; const ballRadius = 15; for (let i = 0; i < ballCount; i++) { balls.push({ x: Math.random() * (canvas.width - 2 * ballRadius) + ballRadius, y: Math.random() * (canvas.height - 2 * ballRadius) + ballRadius, dx: (Math.random() - 0.5) * 8, dy: (Math.random() - 0.5) * 8, color: `hsl(${Math.random() * 360}, 80%, 60%)` }); } function drawHexagon(radius, angle) { ctx.save(); ctx.translate(centerX, centerY); ctx.rotate(angle); ctx.beginPath(); for (let i = 0; i < 6; i++) { const x = radius * Math.cos(i * Math.PI / 3); const y = radius * Math.sin(i * Math.PI / 3); if (i === 0) { ctx.moveTo(x, y); } else { ctx.lineTo(x, y); } } ctx.closePath(); ctx.strokeStyle = '#fff'; ctx.lineWidth = 3; ctx.stroke(); ctx.restore(); } function isPointInHexagon(x, y, radius, angle) { // Transform point to hexagon's coordinate space const dx = x - centerX; const dy = y - centerY; const cos = Math.cos(-angle); const sin = Math.sin(-angle); const tx = dx * cos - dy * sin; const ty = dx * sin + dy * cos; // Check if point is inside hexagon const sideLength = radius; const height = sideLength * Math.sqrt(3) / 2; const width = sideLength * 2; if (Math.abs(tx) > width / 2) return false; if (Math.abs(ty) > height) return false; const slope = height / (width / 2); const boundary = height - slope * Math.abs(tx); return Math.abs(ty) <= boundary; } function updateBalls() { for (let ball of balls) { // Update position ball.x += ball.dx; ball.y += ball.dy; // Check collision with hexagon const nextX = ball.x + ball.dx; const nextY = ball.y + ball.dy; if (!isPointInHexagon(nextX, ball.y, hexagonRadius, hexagonAngle)) { ball.dx *= -1; } if (!isPointInHexagon(ball.x, nextY, hexagonRadius, hexagonAngle)) { ball.dy *= -1; } // Check collision with other balls for (let otherBall of balls) { if (ball === otherBall) continue; const dx = otherBall.x - ball.x; const dy = otherBall.y - ball.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < ballRadius * 2) { // Simple collision response const angle = Math.atan2(dy, dx); const targetX = ball.x - Math.cos(angle) * ballRadius * 2; const targetY = ball.y - Math.sin(angle) * ballRadius * 2; const ax = (targetX - otherBall.x) * 0.1; const ay = (targetY - otherBall.y) * 0.1; ball.dx -= ax; ball.dy -= ay; otherBall.dx += ax; otherBall.dy += ay; } } } } function drawBalls() { for (let ball of balls) { ctx.beginPath(); ctx.arc(ball.x, ball.y, ballRadius, 0, Math.PI * 2); ctx.fillStyle = ball.color; ctx.fill(); ctx.closePath(); } } function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); // Update hexagon rotation hexagonAngle += 0.005; // Draw hexagon drawHexagon(hexagonRadius, hexagonAngle); // Update and draw balls updateBalls(); drawBalls(); requestAnimationFrame(animate); } // Handle window resize window.addEventListener('resize', () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight; centerX = canvas.width / 2; centerY = canvas.height / 2; hexagonRadius = Math.min(canvas.width, canvas.height) * 0.4; }); animate(); </script> </body> </html> ``` This code creates: 1. A black background canvas 2. A white hexagon that rotates slowly 3. 10 colorful balls that bounce around inside the hexagon 4. Collision detection between balls and the hexagon walls 5. Simple collision response between balls The animation uses requestAnimationFrame for smooth rendering. The hexagon rotates continuously while the balls bounce around, changing direction when they hit the hexagon walls or other balls. You can adjust parameters like: - Number of balls (ballCount) - Ball size (ballRadius) - Hexagon size (hexagonRadius multiplier) - Rotation speed (hexagonAngle increment) - Ball speed (dx/dy values)
Drag to resize
```html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Bouncing Balls in Spinning Hexagon</title> <style> /* Add some basic styling to our canvas */ canvas { border: 1px solid black; } </style> </head> <body> <canvas id="canvas" width="600" height="600"></canvas> <script> // Get the canvas element const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); // Define some constants const NUM_BALLS = 10; const HEXAGON_SIZE = 200; const BALL_RADIUS = 10; // Define the hexagon's center and rotation angle let centerX = canvas.width / 2; let centerY = canvas.height / 2; let angle = 0; // Define the balls' initial positions and velocities let balls = []; for (let i = 0; i < NUM_BALLS; i++) { balls.push({ x: centerX + Math.random() * HEXAGON_SIZE - HEXAGON_SIZE / 2, y: centerY + Math.random() * HEXAGON_SIZE - HEXAGON_SIZE / 2, vx: Math.random() * 4 - 2, vy: Math.random() * 4 - 2, }); } // Main animation loop function animate() { // Clear the canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // Draw the spinning hexagon ctx.save(); ctx.translate(centerX, centerY); ctx.rotate(angle); ctx.beginPath(); for (let i = 0; i < 6; i++) { let a = i * Math.PI / 3; let x = HEXAGON_SIZE / 2 * Math.cos(a); let y = HEXAGON_SIZE / 2 * Math.sin(a); ctx.lineTo(x, y); } ctx.closePath(); ctx.stroke(); ctx.restore(); // Update and draw the balls for (let ball of balls) { // Update the ball's position ball.x += ball.vx; ball.y += ball.vy; // Bounce off the hexagon's edges let dx = ball.x - centerX; let dy = ball.y - centerY; let dist = Math.sqrt(dx * dx + dy * dy); let angle = Math.atan2(dy, dx); if (dist > HEXAGON_SIZE / 2 - BALL_RADIUS) { // Normalize the ball's position to be on the hexagon's edge ball.x = centerX + (HEXAGON_SIZE / 2 - BALL_RADIUS) * Math.cos(angle); ball.y = centerY + (HEXAGON_SIZE / 2 - BALL_RADIUS) * Math.sin(angle); // Bounce the ball let normalX = Math.cos(angle); let normalY = Math.sin(angle); let dotProduct = ball.vx * normalX + ball.vy * normalY; ball.vx -= 2 * dotProduct * normalX; ball.vy -= 2 * dotProduct * normalY; } // Draw the ball ctx.beginPath(); ctx.arc(ball.x, ball.y, BALL_RADIUS, 0, 2 * Math.PI); ctx.fill(); } // Update the hexagon's rotation angle angle += 0.01; // Request the next frame requestAnimationFrame(animate); } // Start the animation animate(); </script> </body> </html> ``` This code creates a canvas element and gets a 2D drawing context. It defines some constants for the number of balls, the size of the hexagon, and the radius of the balls. The `animate` function is called repeatedly using `requestAnimationFrame`, and it updates the positions of the balls, draws the spinning hexagon, and draws the balls. The balls bounce off the edges of the hexagon.
Drag to resize
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Bouncing Balls in Spinning Hexagon</title> <style> #canvas { border: 1px solid black; } </style> </head> <body> <canvas id="canvas" width="600" height="600"></canvas> <script> // Get the canvas element const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); // Set the canvas dimensions canvas.width = 600; canvas.height = 600; // Define the hexagon properties const hexagon = { x: canvas.width / 2, y: canvas.height / 2, radius: 200, rotation: 0, rotationSpeed: 2 }; // Define the ball properties const balls = []; for (let i = 0; i < 10; i++) { balls.push({ x: Math.random() * canvas.width, y: Math.random() * canvas.height, radius: 10, velocityX: Math.random() * 5 - 2.5, velocityY: Math.random() * 5 - 2.5, color: `rgb(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255})` }); } // Main animation loop function animate() { // Clear the canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // Draw the spinning hexagon ctx.save(); ctx.translate(hexagon.x, hexagon.y); ctx.rotate(hexagon.rotation * Math.PI / 180); ctx.beginPath(); for (let i = 0; i < 6; i++) { const angle = i * Math.PI / 3; ctx.lineTo(hexagon.radius * Math.cos(angle), hexagon.radius * Math.sin(angle)); } ctx.closePath(); ctx.strokeStyle = 'black'; ctx.stroke(); ctx.restore(); // Update and draw the balls for (let i = 0; i < balls.length; i++) { const ball = balls[i]; // Update ball position ball.x += ball.velocityX; ball.y += ball.velocityY; // Bounce off the edges if (ball.x - ball.radius < 0 || ball.x + ball.radius > canvas.width) { ball.velocityX *= -1; } if (ball.y - ball.radius < 0 || ball.y + ball.radius > canvas.height) { ball.velocityY *= -1; } // Check collision with the hexagon const distanceToCenter = Math.sqrt((ball.x - hexagon.x) ** 2 + (ball.y - hexagon.y) ** 2); if (distanceToCenter < hexagon.radius) { // Calculate the normal vector of the hexagon at the collision point const angleToBall = Math.atan2(ball.y - hexagon.y, ball.x - hexagon.x); const normalX = Math.cos(angleToBall); const normalY = Math.sin(angleToBall); // Reflect the ball's velocity over the normal vector const dotProduct = ball.velocityX * normalX + ball.velocityY * normalY; ball.velocityX = ball.velocityX - 2 * dotProduct * normalX; ball.velocityY = ball.velocityY - 2 * dotProduct * normalY; } // Draw the ball ctx.beginPath(); ctx.arc(ball.x, ball.y, ball.radius, 0, 2 * Math.PI); ctx.fillStyle = ball.color; ctx.fill(); } // Update the hexagon rotation hexagon.rotation += hexagon.rotationSpeed; // Request the next animation frame requestAnimationFrame(animate); } // Start the animation animate(); </script> </body> </html> ```
Drag to resize
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Bouncing Balls in Spinning Hexagon</title> <style> canvas { border: 1px solid black; display: block; margin: 0 auto; } </style> </head> <body> <canvas id="myCanvas" width="500" height="500"></canvas> <script> const canvas = document.getElementById('myCanvas'); const ctx = canvas.getContext('2d'); const centerX = canvas.width / 2; const centerY = canvas.height / 2; const radius = 200; // Radius for the circular boundary and hexagon size const numBalls = 10; const ballRadius = 10; const balls = []; // Initialize balls with random positions and velocities for (let i = 0; i < numBalls; i++) { balls.push({ x: centerX + (Math.random() - 0.5) * radius * 0.8, y: centerY + (Math.random() - 0.5) * radius * 0.8, vx: (Math.random() - 0.5) * 5, vy: (Math.random() - 0.5) * 5 }); } let angle = 0; // Rotation angle for the hexagon function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); // Draw the spinning hexagon ctx.save(); ctx.translate(centerX, centerY); ctx.rotate(angle); ctx.beginPath(); for (let i = 0; i < 6; i++) { let hexagonAngle = i * Math.PI / 3; let hx = radius * Math.cos(hexagonAngle); let hy = radius * Math.sin(hexagonAngle); if (i === 0) { ctx.moveTo(hx, hy); } else { ctx.lineTo(hx, hy); } } ctx.closePath(); ctx.strokeStyle = 'blue'; ctx.lineWidth = 2; ctx.stroke(); ctx.restore(); // Update and draw balls balls.forEach(ball => { ball.x += ball.vx; ball.y += ball.vy; // Check collision with circular boundary let dx = ball.x - centerX; let dy = ball.y - centerY; let distance = Math.sqrt(dx * dx + dy * dy); if (distance + ballRadius > radius) { // Simple bounce: Reverse velocity ball.vx = -ball.vx; ball.vy = -ball.vy; // Adjust position to prevent sticking let bounceAngle = Math.atan2(dy, dx); ball.x = centerX + (radius - ballRadius) * Math.cos(bounceAngle); ball.y = centerY + (radius - ballRadius) * Math.sin(bounceAngle); } // Draw the ball ctx.beginPath(); ctx.arc(ball.x, ball.y, ballRadius, 0, Math.PI * 2); ctx.fillStyle = 'red'; ctx.fill(); }); angle += 0.01; // Increment rotation angle requestAnimationFrame(animate); } animate(); </script> </body> </html> ```
Drag to resize