{
  "schemaVersion": 1,
  "id": "storm-crowd",
  "version": "1.0.0",
  "status": "published",
  "kind": "effect",
  "title": "Storm Crowd",
  "summary": "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.",
  "description": "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.",
  "role": "Effect (3D, three.js): a full-frame crowd scene, or the crowd rig alone (createStormCrowd) to drop into your own set.",
  "collections": [
    "hypnotic-loops",
    "satisfying"
  ],
  "tags": [
    "storm",
    "storm trend",
    "crowd",
    "crowd dance",
    "synchronised",
    "sync",
    "head bang",
    "bow",
    "still lead",
    "stand out",
    "uniforms",
    "school",
    "concrete",
    "beat",
    "music video",
    "dance",
    "loop",
    "3d"
  ],
  "synonyms": [
    "STORM crowd",
    "STORM dance",
    "storm II crowd dance",
    "storm video",
    "everyone moves but me",
    "stand still trend",
    "crowd erupts",
    "synced crowd",
    "crowd head bang",
    "standout lead",
    "main character crowd",
    "Yung Lean storm"
  ],
  "useCases": [
    "Your own STORM video: hand the loop to a video model as the motion and composition reference and swap in your face",
    "A beat-synced background for a music drop or a track announcement",
    "A \"stand out from the crowd\" brand or recruiting message"
  ],
  "featured": null,
  "publishedAt": "2026-10-10",
  "implementation": {
    "target": "three",
    "runtime": "browser",
    "language": "javascript",
    "module": "esm",
    "entry": "src/storm-crowd.js",
    "exports": [
      {
        "name": "createStormCrowd",
        "signature": "createStormCrowd({ rows?, perRow?, spacing?, rise?, depth?, outfit?, lead?, beats?, bpm?, start?, energy?, looseness?, contactShadow?, seed? }) => { group, lead, figures, beats, secondsPerBeat, setTime(seconds), dispose() }",
        "description": "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."
      },
      {
        "name": "stormPose",
        "signature": "stormPose(beat, { start?, end?, energy? }) => { bow, head, chest, dip, swing }",
        "description": "The choreography alone: the pose channels of a crowd figure at a given beat, for driving your own rigs."
      },
      {
        "name": "OUTFITS",
        "signature": "OUTFITS: { school, black, white, khaki }",
        "description": "The crowd's uniform colours (jacket, trousers, shirt, tie, shoes)."
      }
    ],
    "reusable": [
      "src/storm-crowd.js"
    ],
    "demo": "demo.js",
    "harness": "index.html"
  },
  "dependencies": [
    {
      "id": "three",
      "version": "0.185.1",
      "reason": "Renderer, instanced meshes, lights and shadows."
    },
    {
      "id": "chrome-material",
      "version": "1.1.0",
      "scope": "demo",
      "reason": "Studio environment light and renderer settings."
    },
    {
      "id": "apw-runtime",
      "version": "1.0.0",
      "scope": "demo",
      "reason": "Demo harness and loop helpers."
    }
  ],
  "params": [
    {
      "key": "outfit",
      "label": "Crowd outfit",
      "type": "select",
      "default": "school",
      "options": [
        {
          "value": "school",
          "label": "School blazers"
        },
        {
          "value": "black",
          "label": "All black"
        },
        {
          "value": "white",
          "label": "White shirts"
        },
        {
          "value": "khaki",
          "label": "Khaki uniforms"
        }
      ]
    },
    {
      "key": "lead",
      "label": "Still figure wears",
      "help": "The one figure who never moves.",
      "type": "select",
      "default": "white",
      "options": [
        {
          "value": "white",
          "label": "White shirt"
        },
        {
          "value": "black",
          "label": "Black"
        },
        {
          "value": "red",
          "label": "Red"
        }
      ]
    },
    {
      "key": "tempo",
      "label": "Tempo",
      "help": "Beats per minute. The storm always lands on the second bar.",
      "type": "select",
      "default": "120",
      "options": [
        {
          "value": "90",
          "label": "90 BPM, heavy"
        },
        {
          "value": "105",
          "label": "105 BPM"
        },
        {
          "value": "120",
          "label": "120 BPM"
        },
        {
          "value": "135",
          "label": "135 BPM, frantic"
        }
      ]
    },
    {
      "key": "energy",
      "label": "Energy",
      "help": "How deep the bows and how hard the head snaps.",
      "type": "number",
      "min": 0.3,
      "max": 1.4,
      "step": 0.05,
      "default": 1
    },
    {
      "key": "looseness",
      "label": "Looseness",
      "help": "0 is perfectly in sync; higher lets each figure land a touch early or late.",
      "type": "number",
      "min": 0,
      "max": 1,
      "step": 0.05,
      "default": 0.35
    },
    {
      "key": "rows",
      "label": "Rows",
      "type": "integer",
      "min": 2,
      "max": 6,
      "step": 1,
      "default": 4
    },
    {
      "key": "light",
      "label": "Light",
      "type": "select",
      "default": "overcast",
      "options": [
        {
          "value": "overcast",
          "label": "Flat overcast"
        },
        {
          "value": "night",
          "label": "Night, red rim"
        },
        {
          "value": "golden",
          "label": "Low golden sun"
        }
      ]
    },
    {
      "key": "drift",
      "label": "Handheld drift",
      "help": "Off keeps the camera locked, as in the original.",
      "type": "boolean",
      "default": false
    },
    {
      "key": "seed",
      "label": "Seed",
      "type": "integer",
      "min": 1,
      "max": 999,
      "step": 1,
      "default": 7
    }
  ],
  "presets": [
    {
      "id": "default",
      "label": "Schoolyard, overcast",
      "params": {}
    },
    {
      "id": "night",
      "label": "Black suits at night, red rim",
      "params": {
        "outfit": "black",
        "lead": "white",
        "light": "night",
        "tempo": "135",
        "energy": 1.15,
        "rows": 5,
        "seed": 21
      }
    },
    {
      "id": "golden",
      "label": "Khaki ranks in low sun, red lead",
      "params": {
        "outfit": "khaki",
        "lead": "red",
        "light": "golden",
        "tempo": "90",
        "energy": 1.25,
        "looseness": 0.15,
        "seed": 33
      }
    },
    {
      "id": "inverse",
      "label": "White shirts, one in black",
      "params": {
        "outfit": "white",
        "lead": "black",
        "tempo": "105",
        "looseness": 0.5,
        "seed": 12
      }
    }
  ],
  "output": {
    "width": 1080,
    "height": 1080,
    "fps": 30,
    "durationSeconds": 8,
    "posterSeconds": 2.2,
    "alpha": false,
    "motion": "procedural",
    "loop": {
      "seamless": true,
      "cycleSeconds": 8,
      "method": "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."
    },
    "seed": {
      "param": "seed",
      "behavior": "Seed sets each figure's small offsets in place and facing, its height, its hair colour and, with looseness above 0, how early or late and how hard it moves: same seed, same crowd."
    }
  },
  "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.",
    "compatibleWith": [
      "kinetic-title",
      "glitch-text",
      "vhs-camcorder",
      "bass-pulse"
    ],
    "notes": [
      "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."
    ]
  },
  "reuse": [
    "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."
  ],
  "integration": {
    "newProject": "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.",
    "existingProject": "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."
  },
  "performance": {
    "notes": "34 figures at about 19 000 triangles each in 15 instanced draw calls, one 2048 shadow map, anti-aliasing. Real time on any GPU; about 5 seconds a frame under software WebGL (SwiftShader).",
    "limits": "Six rows of nine is the largest crowd the parameters allow; the code takes more, but the camera is framed for up to six rows."
  },
  "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": {
    "spdx": "MIT",
    "notice": "Copyright (c) 2026 AI Pixel Wizard. Released under the MIT License. See LICENSE in this folder.",
    "thirdParty": []
  },
  "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).",
  "changelog": [
    {
      "version": "1.0.0",
      "date": "2026-10-10",
      "notes": "Initial release."
    }
  ]
}
