Soap Bubble

glass·bubble·

Claude Opus 5.5$4.30 · 38m00s
Claude Opus 5.5 $4.30 · 38m00s
WinnerCheapest
ChatGPT-6 Astra$5.90 · 35m00s
ChatGPT-6 Astra $5.90 · 35m00s
Fastest

Opus wins. Opus builds a real soap film: a shell that stretches, presses into shared walls and pops into droplets and mist. Astra covers every tool in half the code, but its bubble can only bulge.

Remix in Claude

Review

CriterionOpusAstra
BubbleReal deforming shell with pressure: dents, stretches and necksA sphere that bulges in and out; wobbles but can't truly stretch
Merge & splitBubbles press into a shared wall before fusing; splits pinch at the lineMerges and splits work, with a simpler join
PopRupture, droplet ring, mist and a light rippleRing of 30 droplets and a ripple
SoundRicher layered effects, but no limit on overlapping soundsSimpler tones, capped at 12 at once as the prompt asked
Weight106 KB50 KB, half the code for every tool
Cost$4.30$5.90
Time38m00s35m00s
OverallWinner–

The prompt

Build **"Soap Bubble"**: a real-time, interactive 3D soap bubble that people can **poke**, **drag**, **blow up**, **merge**, **split**, **bounce** and **pop**, with iridescent thin-film colour and **synthesised sound effects**, rendered with native **WebGPU** and **WGSL**.

## Deliverable

- One self-contained `.html` file with all JavaScript, WGSL and CSS inline.
- **No external requests of any kind**: no libraries, CDNs, web fonts, images, textures or audio files. Generate everything procedurally in code (sound included, see Sound). Use system font stacks.
- It must run inside a sandboxed iframe (`sandbox="allow-scripts allow-pointer-lock"`). Do not use `localStorage`, cookies, `alert`, pop-ups or top-level navigation, and wrap any storage access in `try/catch`.
- This must be a live simulation, not a video, a pre-baked animation, a static render or a 2D fake.

## The specimen

A single, large, perfect soap bubble floating in a softly lit studio, like a macro photograph: almost invisible except where light plays on it.

- **Film:** a thin transparent shell with swirling **thin-film interference colours** (magentas, golds, cyans, greens) that flow and drift over the surface, driven by film thickness. Thickness is uneven: it thins towards the top, pools towards the bottom, and slowly drains over time.
- **Look:** mostly clear, with strong Fresnel reflections of a soft window light and a faint rim, a visible refraction of the background through the shell, and a subtle bright spot where the light hits. It must read as a bubble from the default view, not a glass ball and not a coloured plastic sphere.
- Proportions: large and centred, filling about 55 to 65% of a square frame, with a soft contact-free floor reflection or gentle shadow below it.

## Physics

- A deformable bubble: simulate the shell as a **surface mesh** (a subdivided icosphere of about 2,000 to 5,000 vertices) with **surface tension**, internal **pressure / volume preservation** and damping, solved with a stable method (XPBD or similar). Render it smooth, with correct normals.
- **Fixed simulation timestep** with bounded substeps, decoupled from frame rate. On a slow frame, drop simulation time rather than exploding.
- **Stability is not optional:** no inverted triangles, no explosions, no permanent collapse, no jitter at rest, and no passing through the floor or walls.
- The bubble is **very light**: it drifts, rotates slowly and bobs on gentle air currents. Contact is soft and slightly sticky.
- After any disturbance it **wobbles** (an oscillation of the shape through several decreasing cycles, ringing like a real bubble) and returns to a sphere within a few seconds.

## Actions

Dragging the empty background orbits within limits. Everything else works directly on the bubble, with a tool picker for **Poke**, **Drag**, **Blow**, **Split** and **Wind**, plus automatic behaviours (Merge, Bounce, Pop). The default tool is Poke.

### Poke
- A tap presses a dimple that rebounds with a wobble spreading outward over the film, and the film colours swirl away from the touch point. Hard presses stretch the film thin; **press too hard or too fast and the bubble pops.**

### Drag
- Press and hold to grab the surface under the cursor and pull. The bubble stretches towards the cursor, with the film thinning and colours shifting as it stretches, and **snaps back and jiggles** on release. Pull far enough and it pops. Flicking keeps momentum.

### Blow
- Press and hold anywhere to inflate the bubble: a stream of air from the cursor grows it, with film getting thinner and brighter as it grows. Releasing lets it float free. Over-inflate and it pops. A small bubble can be grown from nothing: when no bubble exists, holding Blow creates a new one at the cursor.

### Merge
- When two bubbles touch, they **stick**, flatten against each other into a shared wall (a lens-shaped membrane), then fuse into one bubble if the wall is poked or after a short time. The smaller bubble's interior bulges into the larger one, as in real soap films. Support at least **6 bubbles** at a time (cap and ignore extras gracefully).

### Split
- With the Split tool, drag a short line across a bubble to pinch it in two: it **necks in at the line** and divides into two smaller bubbles that wobble apart and settle. Volume is conserved across the split.

### Bounce
- Bubbles that meet the floor or the invisible walls **squash slightly and bounce** with a soft, damped rebound and a wobble, and drift off gently. They never stick to the floor for more than a moment, and they never leave the view.

### Wind
- With the Wind tool, moving the pointer pushes air: bubbles drift and **rotate** away from the cursor, with the film swirling in the direction of the flow.

### Pop
- A pop is a short, convincing event: the film **ruptures from the contact point** and the hole races across the shell in about 0.15 s, the shell retracts into **a ring of fine droplets and a quick mist** that fall and fade, and a brief ripple of light crosses the background. Pop is permanent for that bubble, but the toy **reforms**: about 1 s later a new bubble inflates from nothing at the centre.
- Pops cap at a small number of droplets per bubble so the effect stays smooth.

Use pointer events with **pointer capture** so drags never drop, and support mouse, pen and touch (including a second finger on mobile without breaking the first drag).

## Sound

Every action has a **synthesised sound effect**, generated live with the **Web Audio API** (oscillators, filtered noise, envelopes). **No audio files, no samples, no external requests.**

- **Poke:** a soft, short, rubbery "bloop" (a quick sine sweep with a soft envelope), pitched by how hard and how fast the poke is.
- **Drag / stretch:** a faint, rising squeak or creak while the film stretches, rising with tension, and a springy "boing" on snap-back.
- **Blow:** soft filtered-noise airflow while blowing, with a gentle pitch rise as the bubble grows.
- **Merge:** a small wet "blup" as bubbles join.
- **Split:** a quick pinch squeak followed by two slightly different "bloop" tones.
- **Bounce:** a soft muffled "thup" on the floor, quieter with each bounce.
- **Wind:** a very quiet whoosh that follows pointer speed.
- **Pop:** a sharp, bright, short **"pop"** (a click transient plus a quick band-passed noise burst and a tiny high-pitched tail), followed by the soft patter of droplets, with a little random variation so no two pops sound identical.
- **Reform:** a gentle rising shimmer as the new bubble inflates.
- **Ambient (optional):** a very quiet, soft room tone is allowed but must never be annoying.

Rules:
- Create the `AudioContext` only after the first user gesture (browsers block it before that). Handle a `suspended` context by resuming it on interaction, and wrap every audio call in `try/catch` so a failure can never break the toy.
- A **mute button** with a speaker icon, **sound on by default**, and the `M` key toggles it. A short, clear, unobtrusive master volume, with a master gain, a limiter / compressor and no clipping.
- Limit simultaneous voices (about 12) so rapid actions never turn into noise or glitch.
- Spatial feel: pan each sound slightly to match the bubble's horizontal position on screen.

## Camera and keys

- **Camera:** dragging the empty background orbits within limits; the wheel or a pinch zooms within limits; double-clicking the background resets the view. The default is a slightly low three-quarter view that shows the bubble floating above the floor.
- **Keyboard:** `1` Poke, `2` Drag, `3` Blow, `4` Split, `5` Wind, `M` mute, Space pauses or resumes, R resets.

## Rendering (WebGPU and WGSL)

Distinct, believable thin-film material. Avoid both "clear glass ball" and "opaque coloured sphere".

- **Thin-film interference:** compute colour from film thickness and view angle (a physically inspired spectral or approximated interference palette), with swirling advection driven by the shell's motion and by gravity-driven drainage.
- **Fresnel:** strong reflections at grazing angles, weak at the centre, so the middle looks nearly transparent.
- **Refraction:** screen-space refraction of the background and floor through the shell, slight dispersion at the edge.
- **Lighting:** a seamless soft studio with a large window-shaped key light that is clearly **reflected** in the bubble (the window's shape should be recognisable), a subtle rim light, and a gentle tint from the environment. Tone mapping so highlights never blow out.
- **Pop:** droplets rendered as tiny refractive spheres or bright points, with a brief screen-space ripple.
- Compute shaders are welcome (for the solver, normals, film thickness or droplets), but correctness and stability come first.
- Build pipelines once at start-up and update buffers in place. Preallocate for the caps above (bubbles, droplets) so interaction never creates shaders or pipelines. Handle resize and device-pixel ratio (cap at 2).
- If WebGPU is not available, show a clear, well-designed message explaining that the piece needs a WebGPU browser (for example recent Chrome, Edge or Safari) and why. Do not fall back to WebGL or fake it.

## Interface

An editorial page around the toy (generous whitespace, small caps labels, an italic serif display face for the heading) with a modern control card on the right, one restrained accent colour, and no decorative gradients or glassmorphism. The bubble is the hero, and UI must never cover it on desktop or mobile (on narrow screens, controls collapse into a compact bottom sheet).

**Top left**
- Small caps: `POKEABLE · SPECIMEN 003`
- A large italic serif heading on two lines: *Soap* / *Bubble.*
- Caption lines: "Mostly air." / "Barely there." / "Handle with a fingertip."

**Top right:** a small live status dot with `WEBGPU · LIVE`, a **speaker icon button** for sound (with `ON` / `MUTED`), and `PAUSED` when paused or `WEBGPU UNAVAILABLE` on fallback.

**Right panel: control card "Toolbox"**

Style it as a **floating control card**, not an editorial sidebar: a rounded card (about 20px corners) in an off-white slightly lighter than the page, with a soft drop shadow and no hairline borders, about 240px wide, in the plain system sans-serif font (no serif, no small caps, no uppercase letter-spacing inside the card). A bold sentence-case card title, bold sentence-case section labels, presets and tools as rounded pills or tiles (the active preset filled dark with white text, the active tool tinted and ringed in the accent colour), chunky sliders (a 6px rounded track filled with the accent up to the value and a round white thumb ringed in the accent), live values in small rounded monospace chips, secondary buttons as rounded light-grey chips without borders, toggles as on/off switches (label on the left, switch on the right), and **Pause / Resume** as a solid dark full-width pill. On narrow screens the same card becomes the bottom sheet with rounded top corners.

- The tool picker: **Poke**, **Drag**, **Blow**, **Split**, **Wind**, each with a small drawn icon and its key number. The active tool is clearly marked.
- **Film thickness** slider with its live value (changes the colours and how easily it pops).
- **Surface tension** slider with its live value.
- **Sound** slider (master volume) with its live value.
- Buttons: **New bubble** (adds one, up to the cap), **Reset** (restores a single bubble at rest), **Mute**.
- Switches: **Slow motion** (the simulation at ¼ speed) and **X-ray** (shell wireframe over the surface).
- A **Pause / Resume** button.

**Bottom left**
- A one-line hint that changes with the tool:
  - Poke: "Tap gently. Press too hard and it pops."
  - Drag: "Grab and pull. Let go and it snaps back."
  - Blow: "Hold to inflate. Hold on empty space for a new bubble."
  - Split: "Drag a short line across a bubble to pinch it in two."
  - Wind: "Move the cursor to blow bubbles around."
- Live readouts computed from the simulation: **bubbles**, **film thickness** (nm, illustrative), **volume** (% of rest) and **pops** (count). Values update smoothly, not every frame.

**Bottom right:** a collapsible **"How it works"** section, 4 to 6 plain-English lines explaining the shell mesh, surface tension and pressure, how thin-film colour is computed, how merging and splitting work, and how the sounds are synthesised.

Respect `prefers-reduced-motion`: soften UI transitions and start the scene at rest with no automatic drift. The physics still responds fully to the user.

## Demo mode (required)

If the URL has `?demo=1`, auto-play a 4-second loop with no user input: the object drops in, wobbles, gets poked or sliced once, then settles.

Exactly:
- **Hide all UI** (text, panel, badges, hint, readouts, cursor). Only the bubble, floor and background are visible.
- **Sound is completely off in demo mode:** never create an `AudioContext`, play nothing.
- Keep the default camera fixed, with the bubble filling about 55 to 65% of a square frame.
- A deterministic loop that restarts every 4.0 s of real time from the identical state:
  - **0.0 to 0.7 s:** the bubble floats in from above and settles with a soft bounce on the air.
  - **0.7 to 1.5 s:** a poke dents it and it wobbles, with the film colours swirling away from the touch.
  - **1.5 to 2.4 s:** the bubble is pulled to one side, stretching, then snaps back and jiggles.
  - **2.4 to 3.3 s:** a smaller bubble drifts in, touches and fuses with the large one into a shared wall, then merges.
  - **3.3 to 4.0 s:** everything wobbles down to rest as one smooth bubble. It does **not** pop, so the loop restarts cleanly from the identical state.
- Use no randomness in demo mode. Drive the simulation from real elapsed time with the fixed timestep, so the loop lasts 4 s even at low frame rates.
- Start moving within 1 second of the page loading.

## Before you finish

Self-check against this list and fix anything that fails:
- Try every tool, slider and toggle at both extremes, plus New bubble, Reset, pause and resume, Slow motion, X-ray, keys 1 to 5, M, and resizing to phone width.
- Poke, drag, blow, split, wind, merge, bounce and pop each bubble many ways, including 6 bubbles at once and repeated pops. Nothing explodes, jitters at rest, goes through the floor or leaves the view, and every bubble settles.
- Sound: every action has its effect, nothing plays before the first gesture, mute and volume work, rapid actions never clip or pile up, and a failing audio call never breaks the toy.
- `?demo=1` matches the timeline above, loops cleanly, shows no UI and plays no sound.
- No console or WebGPU validation errors.

Put the quality of the film colours, the wobble and the pop first, with their sounds. It should be something people can't stop poking.