Rainbow Jelly Cake

jellycake

OpusKimi
Opus$8.17 · 50m00s
Claude Opus 5.5$8.17 · 50m00s
Claude Opus 5.5 $8.17 · 50m00s
Winner
Kimi$3.00 · 40m00s
Kimi K3.1 Pro$3.00 · 40m00s
Kimi K3.1 Pro $3.00 · 40m00s
CheapestFastest

Opus wins.

Remix in Claude

The prompt

Build **"Rainbow Jelly Cake"**: a real-time, interactive 3D two-tier birthday cake made entirely of **rainbow jelly** that people **poke**, **slice into wedges**, **light**, **melt** and **blow out**, as ONE self-contained HTML file with **synthesised sound**.

It should feel like a glossy dessert commercial shot in macro: so wet and wobbly you can almost taste it, every touch answered by a slow, heavy, gelatin jiggle, and the cut faces showing six perfect stripes.

## Tech

- three.js r170 with `WebGPURenderer` (automatic WebGL2 fallback). Load it with an importmap from jsDelivr: map "three", "three/webgpu" and "three/tsl" to `https://cdn.jsdelivr.net/npm/three@0.170.0/build/three.webgpu.min.js`, and "three/addons/" to `https://cdn.jsdelivr.net/npm/three@0.170.0/examples/jsm/`. These are the **only** network requests allowed: no other libraries, fonts, images, models, textures or audio files. Use TSL node materials. Physics and simulation are your own code (no physics libraries).
- Start with `<!doctype html>`, `<meta charset="utf-8">` and a viewport meta tag. System font stacks only.
- It must run inside `<iframe sandbox="allow-scripts allow-pointer-lock">` (no `allow-same-origin`): `localStorage` and cookies throw there, so wrap every storage access in `try/catch` and keep values in memory. No `alert`, pop-ups, microphone or top-level navigation.
- Fixed simulation timestep (120 Hz) with bounded substeps, decoupled from frame rate; on a slow frame, drop simulation time rather than exploding. Let the cake sleep once fully at rest (skip the solver) and wake on any input.
- Preallocate every buffer, geometry and material at start-up, sized for the caps below (pieces, drips, smoke, sprinkles); nothing is created during interaction, cutting included.
- A quality scaler that lowers pixel ratio, then shadows, then post effects, then particle counts when frames get slow, never the physics feel. Target 60 fps on a mid-range phone.
- Known three r170 pitfalls, avoid them:
  - the scene background is drawn after transparent objects: draw the backdrop as a mesh (or give transparent objects `depthWrite: true`);
  - never `.clone()` a `CanvasTexture` (the clone uploads black);
  - create every `InstancedMesh` at its full count and never draw one with fewer than 2 instances (wrong-size uniform buffer, black screen);
  - never let a helper overwrite a shared temp vector its caller still uses;
  - on WebGL2 an `InstancedMesh` of roughly 250-1000 instances keeps its matrices in a 16 KB uniform block and fails to compile: keep instanced meshes under 250 (or above 1000).

## Priorities

Build it in this order, and finish each tier properly before starting the next. A polished tier 1 beats a broken everything.

1. **Must be excellent:** the jelly wobble (heavy, slow, layered, top tier lagging the bottom); the jelly material (translucent stripes, gloss, colour deepening with thickness); the knife cutting a real wedge that you pull out; the demo mode.
2. **Must work:** candles with flames, Torch lighting and melting, Blow with smoke, drop, plate tap, the sliders and colour presets, sound, Lights off, the interface.
3. **Polish:** coloured light spilling onto the plate, the wish Easter egg, sprinkles, the cinema mode.

Never ship a feature half-working: if something from tier 3 can't be done well, leave it out cleanly.

## The cake

- **Plate:** a round white glazed ceramic cake plate, about 2x the bottom tier's diameter (room to set a pulled-out slice down beside the cake), with a soft raised rim; on a light warm-oak table. A soft off-white glaze, not a mirror: its highlights must never outshine the jelly.
- **Tiers:** bottom tier diameter 1.0, height 0.55; top tier diameter 0.62, height 0.42, centred. Slightly rounded edges (bevel radius about 0.04), a very slightly domed, wet top on each tier, sides very slightly bulging outward at the base as if settling under their own weight.
- **Layers:** six horizontal stripes through the **whole cake** (bottom tier holds red, orange, yellow, green from the bottom up; top tier holds blue, purple, so the stripes continue upward), each stripe a slightly different thickness, with soft, slightly wavy boundaries and a thin brighter line where two colours meet. Colour deepens where the jelly is thicker, so tier centres are richer than edges.
- **Inside:** 40-80 tiny suspended air bubbles per tier (instanced, they move with the jelly), a faint internal grain, so depth reads.
- **Candles:** five thin spiral candles (diameter 0.025, height 0.18) in a ring on the top tier, striped white and a rainbow pastel, each **embedded in the jelly**: bound to the deforming volume so they lean, tilt and bob with every wobble. They never float or stay fixed in space.
- **Flames:** small teardrop flames (emissive, blue at the base, warm yellow-white above) that flicker, lean against motion (when the cake jiggles right, flames trail left) and give a small warm point light each.

## Physics

- A volumetric soft body: a tetrahedral mesh (about 2,000-5,000 tets for the whole cake) solved with **XPBD**, deviatoric / elastic constraints plus volume preservation, the smooth high-resolution surface skinned to the tets (barycentric embedding).
- **Two tiers, one cake:** the top tier is attached to the bottom one by a soft bond, so a hit on the plate or the bottom tier reaches the top a beat later (about 80-150 ms) and the top tier sways with a slightly longer period. A wave visibly travels upward.
- **Jelly, not rubber:** after any input it wobbles 4-6 times with decreasing amplitude and comes to rest in 2-4 s. Low-frequency, heavy, a little bit of shear; no instant snap-back, no endless buzzing, no high-frequency jitter.
- **Plate:** the cake sticks a little to the plate (friction), slides only when pushed hard, and never sinks into it. Tapping the plate (Hand, tap on the plate rim) sends a small shock up through the cake.
- **Stability is not optional:** no inverted tets (detect and recover), no explosions, no permanent collapse, nothing through the plate. Volume stays within a few percent of rest.
- Every piece returns to its own rest shape (elastic), except where the torch melted it (below).
- **Firmness** changes stiffness smoothly without resetting the shape (soft = slow, deep sag; firm = quick, small wobble). **Wobble** changes damping (how long it keeps jiggling).

## Tools

A tool picker with **Hand** (`1`), **Knife** (`2`), **Torch** (`3`), **Blow** (`4`). A press that ends within 6 px and 450 ms counts as a tap. One finger acts; two fingers or right-drag orbit. Nothing is hover-only.

- **Hand:** tap pokes a dimple that rebounds with a ripple spreading out from the spot. Press and drag grabs the surface point under the cursor and pulls it: deformation is local and follows the finger, stretch is dramatic but capped (never tears). Releasing during a fast drag keeps the momentum (flick). Grab a cut piece to lift it: it sags and stretches under its own weight while carried, and **plops** onto the plate with a squash and a wobble when let go. Double-tap the cake (or `D`) to lift it a little and drop it on the plate: the bottom tier squashes, the top tier lands a beat later. Tap the plate rim to knock it.
- **Knife:** drag a straight line across the cake to cut along the vertical plane under it, top to bottom through both tiers and every layer. The cut is **real and exact**: split every tet the plane crosses into tets on either side of the plane (snap vertices that are very close to it onto it first, to avoid slivers), and give the two sides their own copies of the vertices on the plane, so the cake becomes separate soft bodies that wobble and can be grabbed on their own. The physical cut face must lie exactly on the knife plane: never cut along existing tet faces (a staircase), and never paint a flat cut face over a staircase, which leaves see-through gaps. Pieces from the same cut rest side by side without pushing each other apart or over (let them ignore each other until they have been separated). A cut must never stall the frame: no full rebuilds of every piece, keep it to a few milliseconds. **Two cuts from near the centre outward make a wedge**: the classic birthday slice. A freshly cut wedge slides out a few millimetres by itself and wobbles, so it reads as free. Cut faces show the six stripes in clean cross-section, glossy and slightly wet, with bubbles visible; a candle in a cut piece stays in that piece. Up to about **10 pieces**; extra cuts are ignored gracefully; crumbs where two cuts cross are dropped, and thin slivers stay stable. `X` makes one perfect wedge facing the camera.
- **Torch:** hold to aim a small blue-cored blowtorch flame. Near a candle wick it **lights** it (a little flare and a pop). On the jelly it heats a local **temperature field** (diffusing slowly, cooling over time): hot jelly glows warmer, turns clearer and softer, **droops**, and drips **glossy drops in the local layer colour** that run down the side, fall and set as small domes on the plate. Lit candles also warm the jelly around them very slowly, so a cake left burning for a minute starts to droop around its candles. When the heat goes, the jelly sets again in its new shape. Cap drips at about 40; it can never melt away entirely. `L` lights all candles one after another.
- **Blow:** hold to blow a breath from the cursor towards the cake; strength grows with hold time (shown by a small breath arc). Flames lean, flicker, and go out from the nearest outward, each leaving a curl of soft grey smoke that rises and fades. The cake jiggles from the breath (a gentle push on the near side). A short tap only makes the flames flicker. `B` blows a full breath.

## Easter egg

- **Make a wish:** blowing out all five candles in **one** breath triggers a soft confetti puff of tiny rainbow sprinkles that land, stick to the wet jelly (bound to the surface, they wobble with it) and bounce on the plate, and a toast "Wish made." Sprinkles cap at about 200; Reset clears them.

## Sound

- Synthesize everything with WebAudio; create the `AudioContext` on the first gesture only; a mute button and `M`.
- Poke: a soft wet squelch whose pitch drops with firmness; wobble: a quiet low "wub" that fades with the motion; knife: a short slick slice; wedge plop: a fat wet thud; drip: tiny plinks on the plate; torch: a soft hiss while held, a little "fwip" when a candle lights; blow: a breathy noise swell, a soft puff per candle going out; the wish: a little music-box arpeggio. Nothing clips or piles up (voice limit).

## Look

Aim for a premium dessert commercial, not a game asset.

- **Colour:** sRGB output, **AgX** tone mapping, exposure about 1.0, HDR internally: the jelly colours stay saturated and juicy, never washed out; flames and specular glints are the only things that bloom.
- **Lighting:** a procedural environment (`RoomEnvironment` through `PMREMGenerator`, no HDR files) for the wet reflections, a large soft warm key from the side (daylight from a window, front-left), a soft fill, and a cool rim from behind that outlines the tiers and makes the jelly glow at the edges. Soft shadows (PCF soft, 2048, 1024 on phones) and a contact shadow under the plate and under the cake on the plate.
- **Coloured light through the jelly:** the jelly's shadow on the plate and table is **tinted** by the layers, not grey: blend a projected, slowly wobbling colour pattern (a cheap caustic: the six colours, brightest under thin edges, following the cake's deformation) into the shadow area on the plate and table. In **Lights off**, the lit candles make the whole cake glow from inside like a lantern.
- **Materials:**
  - **Jelly:** a physical material with transmission 1, thickness from the real geometry, IOR 1.34, attenuation colour per layer and attenuation distance so tier centres are deep and edges glow, roughness about 0.04, clearcoat 1 for a wet film, a strong Fresnel sheen at grazing angles, faint procedural roughness variation so it never looks CG-perfect. Never glass-hard, never plastic, never opaque.
  - **Cut faces:** slightly rougher and wetter than the outer surface; **drips:** glossier than the cake.
  - **Plate:** white glaze with clearcoat and a soft sheen; **table:** light warm oak, matte; **candles:** waxy (a little subsurface look via sheen), the wick dark.
- **Post:** a light vignette; flames and the torch get their own soft glow sprites. Bloom only if it touches nothing but the flames: a glossy plate's highlights are as bright as a flame, and a full-screen bloom hazes the whole cake milky. No heavy chromatic aberration or grain.
- **Saturation:** the six colours stay clearly distinct and juicy at the default exposure (yellow reads yellow, purple reads purple, not blue); the cake is never washed out, milky or pastel in the Rainbow preset, and never so deep that it looks opaque.
- **Backdrop:** a warm soft studio gradient (peach-cream to warm beige) in a **mid-light tone, never pure white**; **Lights off** dims it to a warm dark brown, never pure black.
- **Framing:** the default camera shows the cake and plate filling about 75% of a **square** frame, three-quarter view from slightly above, so the top, the candles and the stripes on the side all read at once.
- **Motion:** everything eases, overshoots a little and settles; nothing pops or teleports.

## Interface

- **Top left:** a big bold heading "Rainbow Jelly Cake." with a small subtitle "Six layers. Five candles. Bring a knife."
- **Right:** a rounded control card "Toolbox": the four tools as tiles with a small drawn icon and their key; **Firmness** (soft to firm) and **Wobble** sliders with live values; **Colours** pills: Rainbow, Pastel, Citrus (all six layers change together, smoothly cross-fading); switches for **Slow motion** (¼ speed) and **Lights off**; **Reset** (one whole cake, candles unlit, no drips, no sprinkles).
- **Bottom:** a one-line hint "Tap to poke. Drag to squish. Grab the knife." that changes with the tool (Knife: "Two cuts make a slice. Then grab it."; Torch: "Light the candles. Hold on the jelly to melt."; Blow: "Hold to blow. All five in one breath?") and fades after a few uses.
- A status pill `WEBGPU · LIVE` or `WEBGL2 · LIVE`; toasts for milestones (first slice, all candles lit, wish made), each once. Collapses to a compact bottom sheet at phone width; the UI never covers the cake.
- If neither WebGPU nor WebGL2 works, a clear, designed message.

## Camera and keys

- Orbit around the cake within limits (never under the table), zoom within limits; the cake always stays in frame. `R` reset, `Space` pause, `C` cinema (hide UI, slow orbit), `M` mute, `O` Lights off, `D` drop, `X` wedge, `L` light candles, `B` blow, `1`-`4` tools.
- Respect `prefers-reduced-motion`: softer UI transitions, slower cinema orbit; the physics still responds fully.

## Test hook

If the URL has `?test=1`, expose `window.__toy` with no visible change: `poke(x, y)`, `slice(x1, y1, x2, y2)`, `wedge()`, `drop()`, `lightAll()`, `blow(strength)`, `reset()`, and `getState()` returning `{ renderer, fps, pieces, candlesLit, drips, sprinkles, peakTemp, volumeRatio, kineticEnergy, asleep }`.

## 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.

For this toy that means:
- Hide all UI and the cursor. Never create an `AudioContext`.
- One fixed quality preset, fixed seed, default camera, Rainbow colours, candles lit from the start.
- A deterministic loop restarting every 4.0 s of real time from the identical state:
  - **0.0 to 0.8 s:** the whole cake drops onto the plate from a small height: the bottom tier squashes, the top tier lands a beat later, the flames trail.
  - **0.8 to 2.0 s:** a knife makes two quick cuts from the centre towards the camera; the wedge comes free.
  - **2.0 to 3.2 s:** the wedge slides out towards the camera, stretching a little, and plops on the plate showing six clean stripes; the cake shivers.
  - **3.2 to 4.0 s:** a breath blows the five candles out with small curls of smoke and everything wobbles down, fading back to the state at 0.0 s.
- Start moving within 1 s of load; the action must read clearly at 15 fps (the demo is recorded in a slow software renderer).
- Support `?demo=1&t=<seconds>` to freeze the demo at that time.

## Before you finish

Self-check against this list and fix anything that fails:
- Poke, squish, flick and drop the cake at Firmness soft and firm, Wobble low and high: it always jiggles like gelatin, the top tier lags the bottom, and it always settles.
- Cut it many ways: five quick strokes in a row, straight through, wedges, thin slivers, cuts through candles, cuts through melted areas; grab, carry and drop every piece. Look into every gap from several angles: no see-through holes, no stair-stepped cut faces, no hitch when a cut lands. No inverted tets, nothing through the plate, volume stays near rest.
- Light every candle, melt each side for a long time, blow them out short and long; it never explodes or disappears; the wish works once per lighting.
- Every tool, slider, switch, colour preset, button and key; pause and resume; Lights off; Cinema; resizing to phone width with touch.
- Sound: every action has its effect, nothing plays before the first gesture, mute works.
- It works inside `<iframe sandbox="allow-scripts allow-pointer-lock">` with storage blocked.
- `?demo=1` matches the timeline, loops cleanly, shows no UI and plays no sound; `?test=1` exposes `window.__toy`.
- No console errors or WebGPU validation errors on WebGPU **and** on WebGL2 (force the fallback to check), and it stays smooth on a mid-range phone.

Put the jelly wobble and the wedge first. Pulling out the first slice should be the best part.

More like this