Fireworks · gemma-4-26b-a4b-it
Create a fireworks display on a full-screen canvas where clicks launch shells that explode into gravity-affected, fading particle bursts.
transcript · 4 messages
Create a fireworks display on a full-screen canvas where clicks launch shells that explode into gravity-affected, fading particle bursts.
thinking
The user wants a single-page web app (one `index.html` file) that features a fireworks display.
Key requirements:
- Full-screen canvas.
- Clicks launch shells.
- Shells explode into particle bursts.
- Particles should be affected by gravity and fade out.
- Everything must be in one file (`index.html`).
- No external resources (no CDNs, no network requests).
- Self-contained CSS and JS.
Plan:
1. Create `index.html` structure.
2. Add CSS to make the canvas full-screen and black background.
3. Add JS to:
- Initialize canvas and context.
- Handle resizing.
- Create `Shell` class (launches from bottom towards click position).
- Create `Particle` class (burst from explosion, gravity, friction, fade).
- Implement a main loop for updating and drawing.
- Handle click events.
Implementation details:
- `Shell`:
- Properties: x, y, targetX, targetY, velocityX, velocityY, color, exploded (bool).
- Behavior: Move towards target, check if close enough or reached peak, then explode.
- `Particle`:
- Properties: x, y, velocityX, velocityY, color, life (alpha), decay, gravity, friction.
- Behavior: Move, apply gravity/friction, reduce alpha, remove when dead.
- `FireworkManager` or just global arrays for `shells` and `particles`.
- `requestAnimationFrame` for the loop.
- Use `rgba(0, 0, 0, 0.1)` for clear rect to get a trailing effect.
Let's start writing.index.html (5407 bytes)Successfully wrote 5407 bytes to index.html
thinking
The code looks complete and fulfills all the requirements. - Single page `index.html`. - Full-screen canvas. - Clicks launch shells. - Shells explode into particles with gravity, friction, and fading. - No external resources used. - CSS and JS are inline. I should double check if there are any minor improvements. - The launch point `this.x = width / 2 + (Math.random() * 200 - 100);` might be a bit narrow if the screen is very wide, but it's okay for a demo. - The trail effect `ctx.fillStyle = 'rgba(0, 0, 0, 0.15)';` works well for fireworks. - Gravity and friction are applied to particles. - Shells have a target. One thing: the shell's movement is a straight line to the target. In real life, they follow a parabola, but for a click-to-explode interactive app, straight line is often more intuitive as it "targets" where you clicked. Wait, the prompt says: "clicks launch shells that explode into gravity-affected, fading particle bursts". My implementation: - `Shell` moves from bottom towards click. - Once it hits/reaches the target, it explodes. - `Particle` handles the explosion effect. I'll check the shell's "reached target" condition. `if (distance < 20 || this.y <= this.targetY)` This seems fine. Let's do a quick sanity check of the `Shell` class. If I click near the top, `dy` will be negative (since `targetY < height`). `this.vy` will be negative. `this.y += this.vy` will decrease `y`. When `this.y <= this.targetY` is true, it explodes. Correct. One possible improvement: make the particles look more "sparkly" by using small lines or varying sizes, but `arc` is fine for a simple implementation. I'll add a small feature: launch some fireworks automatically on start so it's not a blank screen. Actually, I'll just leave it as is, it follows the user's instructions directly. Let's check the requirement again: "Build a single-page web app and write it ALL into one self-contained file named index.html in the current directory". I'll provide the final response.
I have created a single-page web application that features a fireworks display. The app uses a full-screen HTML5 canvas. When you click anywhere on the screen, a shell is launched from the bottom area towards your cursor. Upon reaching the destination, the shell explodes into a burst of colorful, gravity-affected particles that gradually fade away. The implementation includes: - **Smooth animations** using `requestAnimationFrame`. - **Particle physics** including gravity, friction, and velocity. - **Visual effects** like trailing through semi-transparent canvas clears. - **Responsive design** that adjusts to your window size. ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Fireworks Display</title> <style> body { margin: 0; padding: 0; overflow: hidden; background-color: #000; cursor: crosshair; user-select: none; } canvas { display: block; } #instructions { position: absolute; top: 20px; left: 20px; color: rgba(255, 255, 255, 0.5); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; pointer-events: none; font-size: 14px; letter-spacing: 1px; } </style> </head> <body> <div id="instructions">CLICK TO LAUNCH FIREWORKS</div> <canvas id="canvas"></canvas> <script> const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); let width, height; let shells = []; let particles = []; function resize() { width = window.innerWidth; height = window.innerHeight; canvas.width = width; canvas.height = height; } window.addEventListener('resize', resize); resize(); class Shell { constructor(targetX, targetY) { // Launch from the bottom center area this.x = width / 2 + (Math.random() * (width * 0.4) - (width * 0.2)); this.y = height; this.targetX = targetX; this.targetY = targetY; const dx = targetX - this.x; const dy = targetY - this.y; const distance = Math.sqrt(dx * dx + dy * dy); const speed = 12 + Math.random() * 5; this.vx = (dx / distance) * speed; this.vy = (dy / distance) * speed; this.color = `hsl(${Math.random() * 360}, 100%, 60%)`; this.exploded = false; } update() { this.x += this.vx; this.y += this.vy; const dx = this.targetX - this.x; const dy = this.targetY - this.y; const distance = Math.sqrt(dx * dx + dy * dy); // Explode if close to target or if it starts falling back if (distance < 15 || this.vy > 0 && this.y > this.targetY) { this.explode(); } } draw() { ctx.beginPath(); ctx.arc(this.x, this.y, 2, 0, Math.PI * 2); ctx.fillStyle = this.color; ctx.fill(); } explode() { this.exploded = true; const particleCount = 60 + Math.floor(Math.random() * 60); for (let i = 0; i < particleCount; i++) { particles.push(new Particle(this.x, this.y, this.color)); } } } class Particle { constructor(x, y, color) { this.x = x; this.y = y; this.color = color; const angle = Math.random() * Math.PI * 2; const speed = Math.random() * 8 + 2; this.vx = Math.cos(angle) * speed; this.vy = Math.sin(angle) * speed; this.gravity = 0.15; this.friction = 0.96; this.alpha = 1; this.decay = Math.random() * 0.015 + 0.01; } update() { this.vx *= this.friction; this.vy *= this.friction; this.vy += this.gravity; this.x += this.vx; this.y += this.vy; this.alpha -= this.decay; } draw() { ctx.save(); ctx.globalAlpha = this.alpha; ctx.beginPath(); ctx.arc(this.x, this.y, 1.5, 0, Math.PI * 2); ctx.fillStyle = this.color; ctx.fill(); ctx.restore(); } } function animate() { // Trailing effect ctx.fillStyle = 'rgba(0, 0, 0, 0.15)'; ctx.fillRect(0, 0, width, height); for (let i = shells.length - 1; i >= 0; i--) { shells[i].update(); shells[i].draw(); if (shells[i].exploded) { shells.splice(i, 1); } } for (let i = particles.length - 1; i >= 0; i--) { particles[i].update(); particles[i].draw(); if (particles[i].alpha <= 0) { particles.splice(i, 1); } } requestAnimationFrame(animate); } window.addEventListener('mousedown', (e) => { shells.push(new Shell(e.clientX, e.clientY)); }); // Launch a few initial ones to show activity setTimeout(() => { shells.push(new Shell(width * 0.3, height * 0.3)); shells.push(new Shell(width * 0.7, height * 0.2)); }, 500); animate(); </script> </body> </html> ```
TAKE IT TO CODE
A single self-contained HTML file — copy it and open in any browser, or reuse the prompt with your own pi-agent run.
RUN
EVALUATION
Blank black canvas throughout; only faint 'Click to launch fireworks' text visible, no fireworks ever appear.
Vision grade · anthropic/claude-sonnet-5
MEASUREMENTS
Raw readings from the capture, not a verdict — the grade above is the only judgement. Percentages are how much of the screen changed; the measure under-reports sparse motion on a dark background.