oneshotlm
Compare all models on this prompt →

Mandelbrot explorer · laguna-xs-2.1

Render the Mandelbrot set with a smooth color gradient on a full-screen canvas, with click-and-drag to pan and mouse-wheel to zoom.

⛨ sandboxed Open in new tab ↗
transcript · 8 messages
user

Render the Mandelbrot set with a smooth color gradient on a full-screen canvas, with click-and-drag to pan and mouse-wheel to zoom.

assistant
thinking
The user wants me to create a single-page web app that renders the Mandelbrot set with:
1. Smooth color gradient
2. Full-screen canvas
3. Click-and-drag to pan
4. Mouse-wheel to zoom

All in one self-contained HTML file with inline CSS and JS, no external resources.

Let me design this:
- A full-screen canvas element
- JavaScript to compute the Mandelbrot set
- Smooth coloring using normalized iteration count
- Panning via click-and-drag
- Zooming via mouse wheel

The Mandelbrot set is computed by iterating z = z² + c for each point c in the complex plane, where z starts at 0. If the magnitude of z exceeds 2, the point is outside the set. The number of iterations before escape determines the color.

For smooth coloring, I'll use the "normalized iteration count" which adds a fractional part based on the final magnitude of z.

Let me write this complete application.

I'll create a complete single-page web app for rendering the Mandelbrot set with smooth coloring, panning, and zooming.

write /workspace/index.html (8409 bytes)
toolResult

Successfully wrote 8409 bytes to /workspace/index.html

assistant
thinking
The file has been written successfully. Let me verify it exists and is correct.
bash {"command":"ls -la /workspace/index.html"}
toolResult

-rw-r--r-- 1 node node 8409 Jul 22 16:47 /workspace/index.html

assistant
thinking
The file has been created. Let me verify the content is correct by reading it.
read {"path":"/workspace/index.html"}
toolResult

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Mandelbrot Set Explorer</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { overflow: hidden; background: #000; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; cursor: crosshair; } #info { position: fixed; top: 10px; left: 10px; color: white; font-size: 14px; background: rgba(0, 0, 0, 0.5); padding: 10px; border-radius: 5px; pointer-events: none; } #info h3 { font-size: 16px; margin-bottom: 5px; } #info p { font-size: 12px; } canvas { display: block; } .panning { cursor: grabbing !important; } </style> </head> <body> <div id="info"> <h3>Mandelbrot Set Explorer</h3> <p>Mouse wheel: Zoom in/out</p> <p>Click and drag: Pan</p> <p>Double-click: Reset view</p> <p>Iterations: <span id="iterations">100</span></p> </div> <canvas id="canvas"></canvas> <script> const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); const info = document.getElementById('info'); const iterationsEl = document.getElementById('iterations'); // Complex plane view let view = { x: -0.5, y: 0, scale: 3, maxIter: 100 }; // Mouse state let isDragging = false; let dragStart = { x: 0, y: 0 }; let viewStart = { x: 0, y: 0 }; // Resize canvas to full window function resize() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; } window.addEventListener('resize', resize); resize(); // Compute Mandelbrot with smooth coloring function mandelbrot(cx, cy, maxIter) { let x = 0, y = 0; let iteration = 0; while (x*x + y*y <= 4 && iteration < maxIter) { const xtemp = x*x - y*y + cx; y = 2*x*y + cy; x = xtemp; iteration++; } if (iteration < maxIter) { // Smooth coloring using normalized iteration count const log_mn = Math.log(x*x + y*y) / 2; const nu = Math.log(log_mn / Math.log(2)) / Math.log(2); const smoothIter = iteration + 1 - nu; return smoothIter / maxIter; } return 0; } // Convert normalized iteration to color using HSV-like gradient function getColor(t) { if (t === 0) return { r: 0, g: 0, b: 0 }; // Use smooth hue rotation for nice gradient const hue = (t * 360 + 180); const saturation = 0.8; const value = 0.9; const c = value * saturation; const x = c * (1 - Math.abs((hue / 60) % 2 - 1)); const m = value - c; let r, g, b; if (hue < 60) { r = c; g = x; b = 0; } else if (hue < 120) { r = x; g = c; b = 0; } else if (hue < 180) { r = 0; g = c; b = x; } else if (hue < 240) { r = 0; g = x; b = c; } else if (hue < 300) { r = x; g = 0; b = c; } else { r = c; g = 0; b = x; } return { r: Math.round((r + m) * 255), g: Math.round((g + m) * 255), b: Math.round((b + m) * 255) }; } // Render the Mandelbrot set function render() { const width = canvas.width; const height = canvas.height; const imageData = ctx.createImageData(width, height); const data = imageData.data; const { x, y, scale, maxIter } = view; const aspect = width / height; // Adjust view to maintain aspect ratio const viewWidth = scale; const viewHeight = scale / aspect; for (let py = 0; py < height; py++) { for (let px = 0; px < width; px++) { // Map pixel to complex plane const cx = x - viewWidth/2 + (px / width) * viewWidth; const cy = y - viewHeight/2 + (py / height) * viewHeight; const t = mandelbrot(cx, cy, maxIter); const color = getColor(t); const idx = (py * width + px) * 4; data[idx] = color.r; data[idx + 1] = color.g; data[idx + 2] = color.b; data[idx + 3] = 255; } } ctx.putImageData(imageData, 0, 0); } // Mouse event handlers canvas.addEventListener('mousedown', (e) => { isDragging = true; dragStart = { x: e.clientX, y: e.clientY }; viewStart = { x: view.x, y: view.y }; canvas.classList.add('panning'); }); canvas.addEventListener('mousemove', (e) => { if (!isDragging) return; const dx = (e.clientX - dragStart.x) / canvas.width * view.scale; const dy = (e.clientY - dragStart.y) / canvas.height * (view.scale / (canvas.width / canvas.height)); view.x = viewStart.x - dx; view.y = viewStart.y - dy; render(); }); canvas.addEventListener('mouseup', () => { isDragging = false; canvas.classList.remove('panning'); }); canvas.addEventListener('mouseleave', () => { isDragging = false; canvas.classList.remove('panning'); }); // Zoom with mouse wheel canvas.addEventListener('wheel', (e) => { e.preventDefault(); const zoomFactor = Math.pow(1.2, e.deltaY > 0 ? 1 : -1); const rect = canvas.getBoundingClientRect(); // Mouse position in canvas coordinates const mx = (e.clientX - rect.left) / rect.width; const my = (e.clientY - rect.top) / rect.height; // Adjust view to zoom towards mouse position view.x -= (mx - 0.5) * view.scale; view.y -= (my - 0.5) * view.scale / (canvas.width / canvas.height); view.scale *= zoomFactor; view.maxIter = Math.min(500, Math.max(50, 100 + Math.log(1/view.scale) * 10)); iterationsEl.textContent = Math.floor(view.maxIter); render(); }, { passive: false }); // Double-click to reset canvas.addEventListener('dblclick', () => { view.x = -0.5; view.y = 0; view.scale = 3; view.maxIter = 100; iterationsEl.textContent = view.maxIter; render(); }); // Initial render render(); // Update info on hover canvas.addEventListener('mousemove', (e) => { if (isDragging) return; const rect = canvas.getBoundingClientRect(); const mx = e.clientX - rect.left; const my = e.clientY - rect.top; const aspect = rect.width / rect.height; const viewWidth = view.scale; const viewHeight = view.scale / aspect; const cx = view.x - viewWidth/2 + (mx / rect.width) * viewWidth; const cy = view.y - viewHeight/2 + (my / rect.height) * viewHeight; info.style.display = 'block'; }); canvas.addEventListener('mouseleave', () => { info.style.display = 'none'; }); </script> </body> </html>

assistant
thinking
The file has been created successfully. Let me verify it's complete and correct. The file contains:

1. CSS in a `<style>` tag - all styling for full-screen canvas, info panel, etc.
2. JS in a `<script>` tag - all the Mandelbrot rendering logic
3. No external resources - fully self-contained
4. Full-screen canvas that resizes with the window
5. Smooth color gradient using HSV-based coloring with normalized iteration count
6. Click-and-drag to pan
7. Mouse-wheel to zoom (zooms towards cursor position)
8. Double-click to reset view

The implementation includes:
- Smooth coloring using the normalized iteration count (fractional part based on escape velocity)
- HSV-to-RGB conversion for smooth color gradients
- Aspect ratio handling for proper rendering
- Dynamic iteration count adjustment based on zoom level

The file is 8409 bytes and is ready to use.

The Mandelbrot set explorer is complete. I've created `/workspace/index.html` with: **Features:** - **Full-screen canvas** - automatically resizes with the window - **Smooth color gradient** - uses normalized iteration count (fractional part based on escape velocity) with HSV-to-RGB color conversion - **Click-and-drag to pan** - click anywhere and drag to navigate the complex plane - **Mouse-wheel to zoom** - zooms towards the cursor position for intuitive exploration - **Double-click to reset** - returns to the default view **Technical highlights:** - All CSS in `<style>` tag, all JS in `<script>` tag - No external resources - completely self-contained - Smooth coloring algorithm: `smoothIter = iteration + 1 - log(log|z|²)/log(2)` - HSV-based color palette for vibrant, smooth gradients - Dynamic iteration count adjustment (increases with zoom depth, capped at 500) - Aspect ratio preservation for accurate fractal rendering

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

modellaguna-xs-2.1
vendorPoolside
harnesspi-agent
generated2026-07-22

EVALUATION

4/5 working

Mandelbrot set with smooth gradient colors; drag pans and wheel zooms correctly, click/keys have no effect as expected.

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.

idle motion0.0%
on click0.0%
on drag76.4%
on wheel12.7%
on enter + space0.0%
on arrow keys0.0%
on w/a/s/d0.0%
frame spread62.7 / 255
console errors0
js errors none

METRICS

runtime18.8s
tokens in5.2k
tokens out3.1k
cached11k
cost / run$0.0156