# Satisfying Bounce Vertical "satisfying" videos and animated optical illusions (270 x 480 drawn in HD, x4 = 1080 x 1920 for TikTok / Shorts / Reels), fifteen scenes, each with its own scale or melody. Every bounce, landing or beat is a note (`Recorder`, mixed offline by `Mixer`). English and French (`config/lang.yaml`, `--lang`). | template | scene | what | sound | |---|---|---|---| | `zoom` | `ZoomRingsScene` | rings with openings, the ball escapes ring after ring, the camera glides out (`ZoomView`), rings regenerate outside: infinite zoom | C major pentatonic marimba, a bell chord at each passage | | `gap` | `GapCircleScene` | ball in a circle closed on 3/4; each exit makes the next opening narrower | Ode to Joy (public domain) | | `rings` | `ConcentricScene` | seven rings, each breaks after 3 hits, from the inside | pentatonic, a bell when a ring breaks | | `multiply` | `MultiplyScene` | every wall hit makes a ball, 10 at most (the oldest fades) | the opening of Fur Elise | | `marble` | `MarbleScene` | marbles down zig-zag ramps | one pentatonic note per ramp (marimba) | | `plinko` | `PlinkoScene` | balls through pegs, peg = note by column | pentatonic marimba, bell at the bottom | | `dominos` | `DominoScene` | a spiral of dominoes falls, then stands up again (12 s loop) | rising notes, then soft falling ones | | `pendulum` | `PendulumScene` | pendulum wave, one cycle = 24 s, exact loop | one pentatonic note per pendulum | | `stairs` | `StairsScene` | a shape hops down ever bigger steps, the camera zooms so its screen size is constant: it "grows" without growing | Lydian, each landing lower | | `tunnel` | `TunnelScene` | endless tunnel of twisted squares (6 s loop) | D minor pentatonic phrase of 12 notes | | `spiral` | `SpiralScene` | rotating spiral of dots, rippling sizes (4 s loop) | whole-tone bells | | `checker` | `WaveGridScene` | checkerboard that undulates (4 s loop) | Hirajoshi marimba | | `ripple` | `DotRippleScene` | dots swelling in rings from the middle: 3D look (4 s loop) | Lydian bells | | `cats` | `CatBounceScene` | four round cats show up one by one (every 0.9 s, pop-in + soft meow) then bounce and meow (synthesized meows + purr) | `meow` voice, pentatonic pitches | | `rain` | `RainPondScene` | ASMR pond, slow drops and rings | `drop` voice, minor pentatonic, pad | - `VideoScene`: base (hook caption, counter, closing caption, fade in/out unless `LOOPS`, `Fx`, `ZoomView`, `note()`, `play()`, `pad()`, ball and ring drawing). Params: `seed`, `seconds`, `lang`, `title`, `melody`, `captions`. Texts are Lang keys (English), French in `config/lang.yaml`. - `src/Render/`: `Gfx` (HD shapes: arcs, rings, ellipses, polygons, glow; nothing snapped to the view grid), `ZoomView` (camera: a centre and a scale, zooms in log space with an ease, the physics never sees it), `Fx` (sparks and shock waves in screen space). - `src/Audio/Scale.php`; `src/Lib/Shorts/Voice.php` (pluck, bell, marimba, sine, pad, drop, **meow**, **purr**: generated, no recording; phase is integrated so glides never click), `Mixer` (low-pass, echo, fades, same peak for every video), `Recorder` (live notes use the same voices; while filming nothing is played). - `src/Lib/Physics2d` is the copy of `jumphp/physics` (+ `removeBall`/`removeRing` forget their "left this ring" marks: PHP reuses object ids). - Zoom without touching the physics: bodies keep world coordinates; the scene scales the ball's radius, speed and gravity by 1/zoom so they stay the same ON SCREEN, and `rebase()` multiplies the world by the zoom when the camera rests, so numbers stay small however far it goes. Illusion scenes use whole numbers of periods (`PERIOD`), so the last frame equals the first. ## What I learned about why these videos work (heuristic: sources are mostly blogs of simulation tools, no independent study) - The sound is the content: a bounce that plays the next note of a tune (or a scale) makes sight and sound match; a minimum spacing between notes (80-120 ms) keeps busy moments from smearing; pentatonic scales never clash when notes overlap; soft attacks and tails, no clicks. - A hook in the first second: already moving, stakes visible, one short line that names the payoff ("wait for the escape"). 15-30 s, finished rather than long. - Tension climbs on a curve: openings that narrow, rings that get closer, near misses, then a clearly bigger payoff (burst, bell chord, zoom). - Loops: the last frame equals the first (periodic motion, whole periods) or the end resembles the start; fade through black otherwise. - Clarity at thumbnail size: dark background, bright saturated shapes, one palette, few moving things (multiply was cut from 44 to 10 balls so every bounce can be followed), high frame rate (60), safe margins for the platforms' interface. - Juice: squash and stretch, trails, halos, a soft flash and a shock wave on each hit, a little camera punch on the beat. - Optical illusions work from relative size, repetition and phase (rings of different size and colour, twisted squares, travelling waves). ## Back to the menu Every `VideoScene` has Escape / `back` and a small "<- MENU" button at the top left (`MENU_BUTTON`, mouse click or touch tap, looked at before the tap-to-restart; not drawn nor active while filming). `exit()` silences the voices (`Recorder::stopAll()`); scenes are reused, `enter()` resets all. ## Make the videos ```bash tools/make-videos.sh [template...] # every template, English + French, in parallel (JOBS=6), then the light versions of the site bin/jumphp video:make satisfying-bounce --template zoom --lang fr --seconds 28 --seed 3 # dist/video/satisfying-bounce-