# Storm Crowd — `storm-crowd@1.0.0`

One figure stands perfectly still while rows of identical uniformed figures behind it erupt on the beat, bowing and head-banging in unison. Loops perfectly.

The look of the STORM trend: a locked-off shot of rows of identical figures in matching uniforms on raw concrete steps, a board-marked concrete wall behind, flat overcast light. For the first bar everyone is frozen. On the second bar the crowd detonates: deep bows, heads snapping back, chests popping, head-banging on every beat, all in sync, while the one figure in front, in a white shirt, never moves. Then they settle and stand still again, so the clip loops. Choose the crowd's outfit, what the still figure wears, the tempo, how hard they move, how tightly they keep time, the number of rows and the light. The figures are faceless mannequins: the crowd is the motion and composition part of the trend, ready for a video model to dress with your own face and footage, or to use as it is as a striking stylised loop.

- Kind: effect; status: published; digest sha256:d085c2067972c849802230d8cbd9ea1aba7d6bf05ca2ef7597ba09cb86f61856
- Role: Effect (3D, three.js): a full-frame crowd scene, or the crowd rig alone (createStormCrowd) to drop into your own set.
- Implementation: three; entry `src/storm-crowd.js`
- Files: https://aipixelwizard.com/registry/storm-crowd/1.0.0/<path> (`LICENSE`, `asset.json`, `demo.js`, `index.html`, `src/storm-crowd.js`)
- Manifest JSON: https://aipixelwizard.com/api/v1/visuals/storm-crowd/versions/1.0.0
- Runnable demo: https://aipixelwizard.com/registry/storm-crowd/1.0.0/index.html (open over http; it imports sibling packages by relative path)
- Dependencies (exact): `three@0.185.1` (source) — Renderer, instanced meshes, lights and shadows.; `chrome-material@1.1.0` (demo) — Studio environment light and renderer settings.; `apw-runtime@1.0.0` (demo) — Demo harness and loop helpers.

## Exports
- `createStormCrowd({ rows?, perRow?, spacing?, rise?, depth?, outfit?, lead?, beats?, bpm?, start?, energy?, looseness?, contactShadow?, seed? }) => { group, lead, figures, beats, secondsPerBeat, setTime(seconds), dispose() }` — The crowd as a group of instanced meshes (one per body part, about 1.75 units tall, standing on y = 0 with the rows rising by `rise` and stepping back by `depth`). Add group to a scene with lights and call setTime(seconds) each frame.
- `stormPose(beat, { start?, end?, energy? }) => { bow, head, chest, dip, swing }` — The choreography alone: the pose channels of a crowd figure at a given beat, for driving your own rigs.
- `OUTFITS: { school, black, white, khaki }` — The crowd's uniform colours (jacket, trousers, shirt, tie, shoes).

## Parameters
- `outfit` (select one of school | black | white | khaki, default "school") — Crowd outfit
- `lead` (select one of white | black | red, default "white") — Still figure wears: The one figure who never moves.
- `tempo` (select one of 90 | 105 | 120 | 135, default "120") — Tempo: Beats per minute. The storm always lands on the second bar.
- `energy` (number 0.3–1.4, default 1) — Energy: How deep the bows and how hard the head snaps.
- `looseness` (number 0–1, default 0.35) — Looseness: 0 is perfectly in sync; higher lets each figure land a touch early or late.
- `rows` (integer 2–6, default 4) — Rows
- `light` (select one of overcast | night | golden, default "overcast") — Light
- `drift` (boolean, default false) — Handheld drift: Off keeps the camera locked, as in the original.
- `seed` (integer 1–999, default 7) — Seed

## Composition
- Inputs: Time in seconds; outfit, still figure's colours, tempo, energy, looseness, rows, light, drift and seed.
- Outputs: A full frame rendered in 3D with a shadow map, or, using src/storm-crowd.js alone, a group of instanced meshes you add to your own scene.
- Coordinates: Metres: figures about 1.75 tall stand on y = 0; row r stands at height r × 0.32 and depth −r × 0.95; the still figure stands 0.55 in front of the front row at x = 0. The demo camera looks from z ≈ 8.3 at a point 1.3 high.
- Lighting: A shadow-casting directional key, a hemisphere fill and a rim light per look (flat overcast, night with a red rim, low golden sun), the studio environment from chrome-material for the sheen, and a soft contact shadow under every figure.
- Timing: setTime(seconds) poses every figure as a pure function of time; the routine repeats every `beats` beats. At 120 BPM the crowd is still until 2.0 s, storms until 7.0 s and is still again by 7.6 s.
- Works with: kinetic-title, glitch-text, vhs-camcorder, bass-pulse
- Each body part is one InstancedMesh, so the default crowd of 34 figures is 15 draw calls (14 parts and the contact shadows); outfit colours are per-instance colours.
- The figures are posed with one hierarchy of empties (hips, spine, chest, neck, head, shoulders, elbows, wrists, hips, knees, ankles) whose world matrices are copied into the instances; the knees bend with the feet planted.
- The shirt front and tie are patches cut from a finer copy of the torso surface, so they wrap the chest.

## Output and loop
- 1080×1080, 30 fps, 8 s, procedural
- Seamless loop: yes — Every tempo fits a whole number of beats into the 8 seconds (90 BPM = 12, 105 = 14, 120 = 16, 135 = 18). The crowd stands still for the first bar, the storm runs to two beats before the end, every move has released by then, and the breathing and the optional camera drift are sines of whole cycles, so the last frame meets the first.
- Verified 2026-10-10 in Chrome 154.0.8037.97 headless on Linux 7.0.0-38-generic (x64), Node 22.14.0 (WebGL 2 via SwiftShader (software)); deterministic: yes; loop check: Frame 0 vs end state (progress 1): mean |Δ| 0.0000, max |Δ| 0 of 255.

## Use it
New project: Create a WebGLRenderer with shadows enabled, a directional light that casts shadows and a hemisphere light; scene.add(createStormCrowd({ bpm: 120, beats: 16 }).group) and call crowd.setTime(frame / fps) each frame.

Existing project: Copy src/storm-crowd.js. Add crowd.group to your scene (move or scale the group to fit your set), keep the clip a whole number of `beats` long for a seamless loop, and set bpm to your track's tempo so the hits land on the beat.

Reuse ideas:
- Make your own STORM video: export the default loop and give it to a video model with a photo, asking it to keep the crowd's motion and camera and put your face on the still figure.
- Music drop or track announcement: Black suits at night, red rim, with Kinetic Title Reveal over the top of the frame from the first hit.
- "Stand out from the crowd" ad or recruiting clip: White shirts, one in black, with your line in a lower third.

## Limitations
- The figures are faceless mannequins with simple hair caps, not people: for a photoreal STORM video use the loop as the motion reference for a video model.
- The choreography is one fixed two-bar routine (bows, head snaps, double-time head-bangs); energy, looseness and tempo change how it is performed, not the moves.
- No sound: set the tempo to your track and line the first hit (the start of bar two) up with the drop in your edit.

## License
MIT. Copyright (c) 2026 AI Pixel Wizard. Released under the MIT License. See LICENSE in this folder.

Provenance: Original code written for AI Pixel Wizard on 2026-10-10 by an automated agent (Claude), inspired by the STORM crowd trend on X, TikTok and Reels in September and October 2026, itself based on the GENER8ION "STORM" music video with Yung Lean. No footage, images, models or code from the video or from any source were used: the mannequins are lathe and sphere geometry, the concrete is drawn with seeded Canvas 2D noise, and the choreography is written from the trend's description (a still lead, a frozen crowd that detonates on the beat with bows and head-banging).

