Build a single-file interactive WebGL toy called "Jelly Bum": a translucent gummy-candy sculpture of a buttocks-and-hips torso (cut flat at the waist and mid-thigh, like a mannequin) wearing a string bikini bottom. It stands on a layered cake board. It wobbles like real jelly when you poke it, pull it or shake the table. TECH - One self-contained HTML file. three.js r160 as an ES module from https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js. No addons, no build step, WebGL 2 only, with a friendly fallback message if WebGL 2 is missing. - Write a custom orbit camera with damping and inertia. Drag on the background to rotate, use the wheel or pinch to zoom. Clamp it so the camera never goes below the board. Arrow keys also rotate. - Build a custom studio environment: a mid-grey gradient dome plus a few bright emissive softbox planes, run through PMREMGenerator, so reflections stay contrasty and the jelly reads as saturated candy. Use ACES tone mapping. The scene background must match the page's CSS background exactly. No shadow maps. SHAPE - Define the body as a signed distance field: smooth-union ellipsoids for the waist, hips and two round cheeks (with a visible cleft), plus two tapered round-cone thighs with a gap between them. The top is cut flat with rounded edges and the bottom sits flat on the board. - Mesh it with surface nets (about 0.0165 grid step, roughly 28k vertices). Snap vertices onto the surface, take normals from the SDF gradient, and drop the bottom cap. SOFT-BODY PHYSICS - Use a spring lattice of about 100 nodes (0.19 spacing) inside the body. The bottom layer is glued to the board. Each node has an anchor spring to its rest position plus springs to its neighbours (only where the midpoint is inside the body). Integrate with semi-implicit Euler in about 10 substeps per frame. The wobble should be visible and decay over about 2–3 s. - Deform the mesh on the GPU. Put node rest positions and displacements in a float DataTexture. In the vertex shader (injected via onBeforeCompile into MeshPhysicalMaterial), blend node displacements with normalized Gaussian weights. Compute the analytic Jacobian so normals stay correct. Smoothly glue the deformation to zero at the base. - Poke (tap or click, or Enter on the focused canvas): apply a strong Gaussian velocity impulse into the surface at the hit point. - Pull (drag on the jelly): spring the nearby nodes toward the cursor's offset on a camera-facing plane. Soft-clamp the offset to about 0.5 units. On release the jelly springs back and wobbles. - Picking: sphere-trace the SDF in the deformed space (sample the field at point p - displacement(p)). Show a grab cursor on hover. - Shake the table: a "Shake the table" button plus the Space key. - All scene objects except the lights live in one stage group, and the shake moves that group. - The motion is a quick hop (about 0.05 units, 0.14 s), then a decaying side-to-side rattle (about 4.2 Hz, amplitude about 0.08, about 1.7 s) in a random mostly-sideways direction. - Feed the board's acceleration (finite differences per substep) into every free node as an inertial force, so the jelly lags behind, sways and keeps wobbling after the board stops. - A "Jelly liveliness" meter shows kinetic plus elastic energy as a percentage. LOOK - Jelly: MeshPhysicalMaterial with transmission 1, thickness about 0.85, IOR 1.36, a saturated attenuation colour (distance about 0.75), roughness about 0.07, clearcoat, a light sheen, a faint emissive glow and DoubleSide, so the back faces refract. About 110 tiny white bubbles are suspended inside and move with the deformation. - Flavours: Lagoon (turquoise), Raspberry (magenta-pink), Lime and Mango. Switching flavour animates the colours smoothly and gives the jelly a small bounce. - Bikini: the same mesh pushed out slightly along the normal. The shape is cut out procedurally in the fragment shader from rest-space coordinates: - A cheeky back panel, high-cut at the sides. - A small front triangle and a gusset. - Thin side strings with a dark trim binding. - Small 3D knots with two dangling cord ends on each hip, deformed with the jelly. - Edges anti-aliased with alpha-to-coverage. - Six procedural prints: Leopard rosettes, Zebra wavy stripes, Polka dots, Breton stripes, Gingham and Classic black. The trim and cords match each print. - Cake board: rounded-rectangle extrusions in three layers (cream frosting top, caramel stripe, chocolate biscuit base). Shift it slightly backward so it sits under the cheeks. - Coloured shadow: planar-project the deformed jelly onto the board top and the floor along the key-light direction, tinted with the flavour colour, using multiply blending and a stencil so it never double-darkens. The shadow follows the stage during the shake. Add soft contact darkening under the feet. - The jelly drops in slightly squashed on load, springs into shape, and the camera eases in. - No candies or balls anywhere. UI (English only) - Top-left: a small mono eyebrow "Confectionery physics · No. 7", a big italic serif title "Jelly Bum." with the full stop in the current flavour colour, and a short italic lede: "Gummy curves, skinny strings and a wobble that always comes back." - Bottom-left: "How to play — Poke the jelly. Pull a curve. Shake the table." with the hint "Space shakes it too. Drag the background to turn it, scroll or pinch to zoom." - Right side: a floating rounded control card containing: - A "WebGL 2 · live" badge with a pulsing dot in the flavour colour, and a Sound toggle (WebAudio boing on poke and release, a low thump plus boing on shake; off by default). - "The jelly": a dark "Shake the table" button with a SPACE key hint, plus the liveliness meter. - "Flavour": four candy-sphere swatches. - "Bikini": six pattern swatches. - Sliders for Firmness and Wobble damping. - Fonts: Literata italic (display), Onest (UI) and JetBrains Mono (labels), all from Google Fonts. - Light and dark themes through CSS tokens; the 3D background follows the theme. - On phones the card becomes a scrollable bottom sheet, and the camera reframes the scene into the free area using setViewOffset. - Respect prefers-reduced-motion, keep a visible keyboard focus state, and use touch-action: none on the canvas.
Build a single-file interactive WebGL toy called "Jelly Bum": a translucent gummy-candy sculpture of a buttocks-and-hips torso (cut flat at the waist and mid-thigh, like a mannequin) wearing a string bikini bottom. It stands on a layered cake board. It wobbles like real jelly when you poke it, pull it or shake the table.
TECH
- One self-contained HTML file. three.js r160 as an ES module from https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js. No addons, no build step, WebGL 2 only, with a friendly fallback message if WebGL 2 is missing.
- Write a custom orbit camera with damping and inertia. Drag on the background to rotate, use the wheel or pinch to zoom. Clamp it so the camera never goes below the board. Arrow keys also rotate.
- Build a custom studio environment: a mid-grey gradient dome plus a few bright emissive softbox planes, run through PMREMGenerator, so reflections stay contrasty and the jelly reads as saturated candy. Use ACES tone mapping. The scene background must match the page's CSS background exactly. No shadow maps.
SHAPE
- Define the body as a signed distance field: smooth-union ellipsoids for the waist, hips and two round cheeks (with a visible cleft), plus two tapered round-cone thighs with a gap between them. The top is cut flat with rounded edges and the bottom sits flat on the board.
- Mesh it with surface nets (about 0.0165 grid step, roughly 28k vertices). Snap vertices onto the surface, take normals from the SDF gradient, and drop the bottom cap.
SOFT-BODY PHYSICS
- Use a spring lattice of about 100 nodes (0.19 spacing) inside the body. The bottom layer is glued to the board. Each node has an anchor spring to its rest position plus springs to its neighbours (only where the midpoint is inside the body). Integrate with semi-implicit Euler in about 10 substeps per frame. The wobble should be visible and decay over about 2–3 s.
- Deform the mesh on the GPU. Put node rest positions and displacements in a float DataTexture. In the vertex shader (injected via onBeforeCompile into MeshPhysicalMaterial), blend node displacements with normalized Gaussian weights. Compute the analytic Jacobian so normals stay correct. Smoothly glue the deformation to zero at the base.
- Poke (tap or click, or Enter on the focused canvas): apply a strong Gaussian velocity impulse into the surface at the hit point.
- Pull (drag on the jelly): spring the nearby nodes toward the cursor's offset on a camera-facing plane. Soft-clamp the offset to about 0.5 units. On release the jelly springs back and wobbles.
- Picking: sphere-trace the SDF in the deformed space (sample the field at point p - displacement(p)). Show a grab cursor on hover.
- Shake the table: a "Shake the table" button plus the Space key.
- All scene objects except the lights live in one stage group, and the shake moves that group.
- The motion is a quick hop (about 0.05 units, 0.14 s), then a decaying side-to-side rattle (about 4.2 Hz, amplitude about 0.08, about 1.7 s) in a random mostly-sideways direction.
- Feed the board's acceleration (finite differences per substep) into every free node as an inertial force, so the jelly lags behind, sways and keeps wobbling after the board stops.
- A "Jelly liveliness" meter shows kinetic plus elastic energy as a percentage.
LOOK
- Jelly: MeshPhysicalMaterial with transmission 1, thickness about 0.85, IOR 1.36, a saturated attenuation colour (distance about 0.75), roughness about 0.07, clearcoat, a light sheen, a faint emissive glow and DoubleSide, so the back faces refract. About 110 tiny white bubbles are suspended inside and move with the deformation.
- Flavours: Lagoon (turquoise), Raspberry (magenta-pink), Lime and Mango. Switching flavour animates the colours smoothly and gives the jelly a small bounce.
- Bikini: the same mesh pushed out slightly along the normal. The shape is cut out procedurally in the fragment shader from rest-space coordinates:
- A cheeky back panel, high-cut at the sides.
- A small front triangle and a gusset.
- Thin side strings with a dark trim binding.
- Small 3D knots with two dangling cord ends on each hip, deformed with the jelly.
- Edges anti-aliased with alpha-to-coverage.
- Six procedural prints: Leopard rosettes, Zebra wavy stripes, Polka dots, Breton stripes, Gingham and Classic black. The trim and cords match each print.
- Cake board: rounded-rectangle extrusions in three layers (cream frosting top, caramel stripe, chocolate biscuit base). Shift it slightly backward so it sits under the cheeks.
- Coloured shadow: planar-project the deformed jelly onto the board top and the floor along the key-light direction, tinted with the flavour colour, using multiply blending and a stencil so it never double-darkens. The shadow follows the stage during the shake. Add soft contact darkening under the feet.
- The jelly drops in slightly squashed on load, springs into shape, and the camera eases in.
- No candies or balls anywhere.
UI (English only)
- Top-left: a small mono eyebrow "Confectionery physics · No. 7", a big italic serif title "Jelly Bum." with the full stop in the current flavour colour, and a short italic lede: "Gummy curves, skinny strings and a wobble that always comes back."
- Bottom-left: "How to play — Poke the jelly. Pull a curve. Shake the table." with the hint "Space shakes it too. Drag the background to turn it, scroll or pinch to zoom."
- Right side: a floating rounded control card containing:
- A "WebGL 2 · live" badge with a pulsing dot in the flavour colour, and a Sound toggle (WebAudio boing on poke and release, a low thump plus boing on shake; off by default).
- "The jelly": a dark "Shake the table" button with a SPACE key hint, plus the liveliness meter.
- "Flavour": four candy-sphere swatches.
- "Bikini": six pattern swatches.
- Sliders for Firmness and Wobble damping.
- Fonts: Literata italic (display), Onest (UI) and JetBrains Mono (labels), all from Google Fonts.
- Light and dark themes through CSS tokens; the 3D background follows the theme.
- On phones the card becomes a scrollable bottom sheet, and the camera reframes the scene into the free area using setViewOffset.
- Respect prefers-reduced-motion, keep a visible keyboard focus state, and use touch-action: none on the canvas.