Loop proof: frame 0 vs frame 8.0 pixel-compared with PIL ImageChops.difference — mean diff 0.0, max diff 0. The loop is pixel-perfect on the first build; no property needed re-cycling.
Check passed with one info note: the largest blurred blob intentionally paints past the top canvas edge while orbiting. The overflow is part of the background design and is identical at frame 0 and frame 8.0, so it does not touch the loop.
Nothing else broke — the first render shipped. The discipline that made it work: no fades anywhere (the corner label is static from frame 0) and every pulse runs an even number of half-cycles.
Banned list —Beat grid —Build method —Self-review —
<direction>
An 8-second seamless loop background (1280x720, 30fps) in the accent #ff5c1a, designed to sit BEHIND other content — no headline, no message, nothing that competes with foreground text. House style: near-black #0a0a0a background, white type, ONE orange accent #ff5c1a, Arial/Helvetica bold, no webfonts. Layered soft shapes drift on strictly periodic motion; a small corner label reads CALM over "SEAMLESS LOOP — 8S". The end state must EQUAL the start state, exactly.
Banned: fades in or out, ease curves that do not return to their start value, pulses that are not a whole number of cycles, text larger than a corner label, real brand logos.
</direction>
<structure>
0.0-8.0s (one continuous cycle, no beats): three large blurred circles ride two counter-rotating orbit containers (0 to 360 degrees and 0 to -360 degrees, linear, across the full 8s); a dashed ring rotates 0 to 360 linear and a thin ring counter-rotates inside it; a dot grid drifts exactly one 48px tile; the accent core breathes through two full sine cycles (scale up-down twice, ending at scale 1). The CALM corner label is static from frame 0 — even a fade-in would break the loop.
</structure>
<build>
One HyperFrames composition, 1280x720, seek-driven paused GSAP timeline. Every tween is either a full rotation (360 or -360, ease none), a yoyo pulse whose repeats make whole cycles, or a background-position shift of exactly one tile — audit each animated property and name its cycle count before rendering. Soft shapes are solid-colour divs with a heavy blur filter, in accent and low-opacity white only. Prove the loop afterwards: snapshot at 0s and at 8.0s and pixel-compare (PIL ImageChops.difference) — report the mean and max diff in the run log; anything visible means a property did not complete its cycle.
</build>
<gotchas>
- A fade-in on the label or any layer makes frame 0 differ from frame 8 — loops start at full state, not from black.
- GSAP rotation to 360 is safe; rotation by a "nice" angle like 90 is not, unless it repeats to a full turn inside the duration.
- A yoyo pulse with an odd repeat count ends at the far state, not the start — count the cycles, do not eyeball them.
- Blurred shapes painting off-canvas are fine only if their orbit is a full cycle; a linear drift off-frame never comes back.
</gotchas>
Before you finish: step through the timeline at five points and fix anything that overlaps, clips the frame, or drifts off the beat grid.
A background that is only allowed to move in circles: every layer completes whole cycles inside exactly 8 seconds, and the loop was proven pixel-identical between frame 0 and frame 8.0 rather than eyeballed.