Generate a single-file HTML cinematic reel with the taste bar of a real cinematographer + motion designer, not a “pretty things coder.” The output must earn every element on screen.
Gravitas is named for what it’s supposed to do: give digital things weight.
These override any temptation to just ship features. If a decision violates one of these, redo it.
Every camera move must be motivated. No move exists because it’s on the shot list. Each shot answers: what is this move revealing about the subject or world that the previous shot didn’t? Write the motivation for each shot as a code comment before you write its keyframes.
Movement discipline: cutting a shot is a stronger design move than adding one. If a shot doesn’t earn its duration — if the camera is moving but nothing in the world or subject is changing during that move — kill the shot. Orbits, whip pans, and dolly moves only work when something is happening beneath them. Otherwise it’s panning for panning’s sake. Holds are stronger than orbits. Trust stillness. A 6–8s hold with only weather, breath, and light flicker will out-drama any bullet-time orbit on a static subject.
Variable shot count. Do NOT force 6 shots. The reel can be 3, 4, 5, or 6 shots depending on what the piece needs. Total loop 24–36s. Fewer, longer, earned shots > more, shorter, arbitrary ones.
Dutch tilts are locked or absent — never drifting. A slow drift from 0° to -4° over 5 seconds reads as indecisive. Either the shot IS tilted (frame 1 rotation, held) or it isn’t. Same for hue-rotates, saturation drifts, and other slow parameter changes: commit, don’t drift.
Composition is asymmetric by default. Subject is never dead-center unless the piece is explicitly about symmetry (ritual, confrontation, sacred). Use thirds, weighted asymmetry, or intentional negative space.
One dominant, one subordinate, one accent. In every shot, the eye should land somewhere first, drift somewhere second, and register a third detail. If everything glows equally, nothing reads. Dim, obscure, or de-saturate the non-dominant elements.
Color is composed, not applied. Pick a palette with a temperature axis (warmest point in frame, coolest point in frame) and a dominance ratio (roughly 60/30/10). Name the warmest light source in the scene — that’s where the eye goes.
Two light sources minimum. A single practical light in a dark scene crushes exposure to unreadable. Always add at least one secondary practical (different temperature, different position) to sculpt the subject and lift shadow readability.
Depth via atmospheric perspective, not just blur. Distant elements: lower contrast, cooler temperature, lower saturation, softer edges. Foreground: higher contrast, warmer if lit, sharper. Parallax speeds must match perceived depth.
Grade shifts per shot — but only via cheap filters. Contrast, saturate, brightness only. Never blur() or hue-rotate() in an animated filter on the camera rig — both are GPU-expensive and cause visible chop when interpolated on a scaled/rotated container.
The subject must live even when still. Every subject gets at least two micro-motions: (a) a breath/sway loop on the whole silhouette (subtle translateY or skew, 3–5s cycle), and (b) one detail loop (blink, ember pulse, hair drift, tail flick, fabric ripple, weapon glint). Micro-motion never syncs to camera — it lives on its own clock.
Timing is rhythmic, not metronomic. No even-slot division. Write out shot lengths in seconds first: some want 2–3s (crashes), some want 6–8s (holds, pulls). Total 24–36s.
Every element earns its place. If you can’t say what a prop, sign, or particle contributes to mood or narrative in one sentence, delete it. Frame density is a taste failure, not a feature.
Sound is half the piece. Add a Web Audio ambient bed. Rules:
steps(4) shift is invisible and costs frames.blur() or hue-rotate() in the .rig’s animated filter. Only contrast(), saturate(), brightness(). These are cheap.will-change: transform, filter on the .rig.CSS repeating-linear-gradient rain reads as diagonal slats, not weather. Always use a <canvas> particle system with:
Applies equally to rain, snow, embers, dust motes, floating spores, etc. — pick the particle behavior that fits the territory.
Silhouette-with-two-rim-strokes is the floor, not the ceiling.
Vignette max: inset 0 0 160px rgba(0,0,0,0.55). Anything darker crushes readability.
Baseline brightness in shot filters: brightness(1.0) or higher. Never below 0.95.
Pick ONE display face per piece, from system stacks. Editorial serif for literary/human. Grotesque sans for clinical. Mono for diegetic HUDs only. Never mix more than two.
Default off. Only include if diegetic (represents in-story surveillance/reticle), editorial (title card at open + credits at close, no persistent chrome), or intentional camp. Letterbox always stays with soft inner vignette.
Single HTML file. <style> in <head>. One <canvas id="weather"> for particles; everything else CSS/SVG.
Layer stack inside a .rig container that owns the shotSequence @keyframes:
.sky — deep radial gradients, sets ambient mood.far-silhouette — distant backdrop SVG, blurred + desaturated for atmospheric perspective.set container — perspective-warped walls/columns/arches/dunes converging toward center.shaft + secondary practical — soft blurred gradient beams, mix-blend-mode: screen.key-light — the warmest point in frame; box-shadow layered glows.ground + .specular — highlight where the warm source catches the surface.fog (or dust/haze equivalent) — slow horizontal drift.figure-far — optional secondary story element.subject — silhouette SVG with breath animation on the parent (translateY + skew)<canvas id="weather"> — particle rain/snow/embers/dustOutside .rig:
.vignette — subtle inset shadow that integrates letterbox with frame.grain — static SVG turbulence noise, mix-blend-mode: overlay.bar.top + .bar.bot — 12vh letterbox.title-card — editorial fade-in at open (~3–10% of loop).credits — editorial fade-in at close (~95–99% of loop).audio-toggle — button to start Web Audio.rig has will-change: transform, filter and animates shotSequence on a 24–36s infinite loop. Filter transitions only use contrast(), saturate(), brightness().
Click-to-start button. On click:
setTimeout per 24–36s loop. Sub swell for pull-back reveal (60→40Hz glide, gain up to 0.18, 5s envelope). Skip whip/crash one-shots unless the shot actually earns them.After building, before showing the user:
Start-Process on Windows, open on macOS, xdg-open on Linux).If not provided, ask the user:
gravitas-{territory-slug}-{subject-slug}.html, open in browser, report back with: