Quiet Cosmos Studio · Free guide
Make a 3D space film with Claude, with free tools
A free guide from Quiet Cosmos Studio, the studio behind the Quiet Cosmos space channel. This is how we make our own 3D films: the tools, the steps, the prompts, and the rules we learned from our mistakes.
What you'll make
A 26.7-second vertical film (1080 x 1920, 30 frames a second). It asks one question on its first frame and answers it with true numbers. Our example:
"The Moon just stopped. How long until it hits Earth?"
- The camera rides just behind the Moon. Earth grows above its edge at its true size, as seen from there.
- A clock races in amber, lands on a true value at each stop and rests there for half a second in white.
- Under it, a "KM TO GO" counter falls from 376,000 to 0.
- A chip names the last fact that came true: "PAST HALFWAY", "PAST GPS SATELLITES", "TIDES TEAR AT IT".
- At 23.5 s the clock lands on 4D 19H 36M and the answer appears: "IT HITS IN UNDER 5 DAYS."
- A tick lands on the exact frame of each stop. Nobody speaks.
Every number is worked out from official pages, most of them NASA's fact sheets. If one is wrong, the build stops before a single frame is drawn.
The shape we start from (from research, not proven): the question on frame 1, something arriving by 3 s, then a stop every 2.5 to 2.75 s (we use 2.75), 8 to 10 stops, 20 to 30 s in all, the payoff, and the end. No end card.
What it costs
- ₹0 / $0 for the tools. Every tool below except Claude is free to use, and the film renders on your own computer.
- Claude is the one plan you need. Claude Code needs a Pro, Max, Ultra, Team, Enterprise or Console account. Anthropic's setup page says the free plan doesn't include it (read 8 November 2026).
What you need
- Claude Code, installed and signed in: code.claude.com/docs/en/setup
- Node.js 22 or newer, and FFmpeg. These are HyperFrames' own requirements.
- A check of your computer:
npx --yes hyperframes@0.9.2 doctorlooks for Node.js, FFmpeg and Chrome. If Chrome is missing,npx --yes hyperframes@0.9.2 browser ensuregets it. - Your own eyes and ears. Claude can measure a film. It can't hear it, and no check can judge a look.
The tools and their licences
| Tool | What it does | Licence |
|---|---|---|
| Claude Code | Writes every file, runs every check, fixes what fails | Needs a Claude plan (above) |
| HyperFrames 0.9.2 (github.com/heygen-com/hyperframes) | Turns an HTML page into an MP4, one frame at a time, and checks it | Apache 2.0 |
| three.js (we use r187) | Draws the Moon, Earth and the stars inside the page | MIT |
| GSAP | The animation library HyperFrames works with. Our 3D films don't need it, because every frame is drawn from its own time. We use it for flat cards | GSAP Standard "No Charge" License: free, commercial use included. Not allowed in no-code animation tools that compete with Webflow |
| NASA maps: Blue Marble, Black Marble, the cloud map, the CGI Moon Kit | Earth by day and by night, its clouds, the Moon | Generally not under copyright in the US. Credit NASA, and never suggest NASA made or backs your film |
| USGS Viking Mars mosaic | Mars | Public domain. Credit USGS/NASA |
| Solar System Scope maps (solarsystemscope.com/textures) | The Sun and the other planets | CC BY 4.0: free even for commercial use, credit required |
| Kenney sound packs (kenney.nl): Impact, Interface, Sci-fi | Ticks and hits | CC0: no credit needed |
| Plus Jakarta Sans | The font | SIL Open Font License 1.1 |
| FFmpeg | Mixes the sound and measures the finished file | LGPL 2.1 or later (some builds GPL) |
Pin HyperFrames to an exact version. Five releases came out in one day while we worked. We also turn its telemetry off (step 1).
Step by step
1. An empty folder
mkdir moon-film && cd moon-film
export HYPERFRAMES_NO_TELEMETRY=1
claude
Everything the film uses lives in this folder: code, maps, font, sounds. The film never fetches anything from the web.
2. Facts first
No code yet. Paste this into Claude:
We're making a 26.7-second vertical film (1080x1920, 30 fps) in this folder with HyperFrames 0.9.2.
Its question: "The Moon just stopped. How long until it hits Earth?"
Facts first, no code. Use NASA's Moon and Earth fact sheets (nssdc.gsfc.nasa.gov/planetary/factsheet).
Treat it as an ideal fall from rest: both bodies fall toward each other, so use Earth's GM plus the Moon's.
Contact is when the surfaces touch. Work out the time and the closing speed at 8 moments a viewer would
care about ("over 1 km a second", "past halfway") and at contact. Work it two ways, a closed form and a
step-by-step integration, and show they agree to the second. Take each kind of number from one table and
name it, and anything else (like how high GPS satellites fly) from one official page. For each distance,
say from what. Write each number, its page and today's date into SOURCES.md.
We worked our fall three ways, and the build fails if they disagree. Then open one source page and check one number yourself. Two of our rules came from this step. A research note gave Neptune's light time from a different distance than NASA's table (4 h 09 min against 4 h 11 min). And NASA's Voyager page gives the distance from Earth, when our film needed it from the Sun.
3. One file for every number
Make timeline.mjs: the one place every number and word of the film lives. 801 frames at 30 fps.
Stop i lands on frame floor(82.5 * (i + 1) + 0.5): a 2.75 s grid. Between stops the clock runs straight.
At each stop it lands on the true value and rests there for 15 frames. The hit is frame 705 (23.5 s).
Under the clock, "KM TO GO": the gap between the surfaces, rounded down to 1,000 km above 10,000 km,
to 100 km above 1,000 km, then to 10 km. It must never read more than is left. Under the band, a chip
with the last fact that came true. From the hit on, the chip says only what is true then, and the last
line answers the title's own question. Set every line break by hand.
Then make build.mjs. With node:assert, check that the clock shows each true value on its frame and holds
it, never runs backwards, and that each row and chip changes on its frame, not before. Nothing may be on
screen under 0.83 s or need over 12 characters a second. No word may sit alone on a line. Compare against
the values in SOURCES.md typed in by hand, not against the same code. Run it until it passes.
The page reads this file to draw. The build reads it to check. Change a number, and the build tells you everything it broke.
4. The page and the 3D scene
Make index.html and scene.mjs for HyperFrames 0.9.2.
- Root div: data-composition-id="main" data-start="0" data-duration="26.7" data-width="1080"
data-height="1920" data-fps="30" data-no-timeline. A full-size canvas for three.js.
- A solid black band from the top to y 712 holds the words: the title at y 269, the clock, KM TO GO.
Nothing 3D passes over it. Must-read text stays inside x 65 to 972 and y 269 to 1248, in capitals
at least 48 px tall (the clock's at least 86).
- Everything local: three.js r187's three.module.js and three.core.js in lib/, Plus Jakarta Sans as a
woff2 in fonts/, maps in assets/. Download NASA's Blue Marble, Black Marble and cloud maps and the CGI
Moon Kit from their official pages, and record each file's page, licence and SHA-256 in SOURCES.md.
No web address anywhere in the code.
- Draw each frame from its time alone, in an "hf-seek" listener: no Math.random, Date, timers or
requestAnimationFrame. Stars from a fixed seed (mulberry32), as soft round dots. Register the setup
as a window.__hf.buildReady promise.
- Start with a full-screen magenta div. Hide it only after the scene and every map are built and the
page has checked every text state against the safe area. If anything fails, throw, so it stays magenta.
- Camera just behind the Moon's far side. Earth grows above its edge at its true angular size from
the Moon (assert it on every frame) and turns at its true rate. After the hit, pull back to the whole
Earth, with something still moving to the last frame.
- Add index.motion.json so HyperFrames' check proves each text element shows by 0.1 s and stays in frame.
Watch it as you go: npx --yes hyperframes@0.9.2 preview opens it in your browser, so you can scrub through it.
Why magenta: one of our renders came out 100% magenta from a missing map, and the render still said it succeeded. A magenta frame in your file means the scene didn't build.
5. Sound
Download Kenney's CC0 packs Impact Sounds, Interface Sounds and Sci-fi Sounds from kenney.nl into
assets/sfx/src. My bed track is assets/sfx/src/bed.mp3. Write make-sound.sh with ffmpeg: one tick on the
exact frame of each stop, each a little higher and heavier than the last; the bed thinning toward the end,
its energy above 110 Hz; silence just before the hit; a low hit at 23.5 s. Place each file with its own
<audio> tag, data-start and data-track-index. Then measure, don't guess: each tick within one frame of its
stop and at least 3 dB over the bed before it; the hit at least 3 dB over the bed in a phone's band
(200 Hz to 6 kHz); loudness -16 to -14 LUFS; true peak no higher than -1.5 dB.
I will listen and decide. You can't hear it.
Pick the bed yourself, and read its own page first (see "Honest limits").
6. Render, with every check
Write render.sh (bash, set -euo pipefail, HYPERFRAMES_NO_TELEMETRY=1). Stop at the first failure:
1. node build.mjs
2. grep scene.mjs and timeline.mjs for Math.random, Date, timers, requestAnimationFrame, fetch or web
addresses, and fail on any
3. npx --yes hyperframes@0.9.2 lint --json, and fail on any error or warning
4. npx --yes hyperframes@0.9.2 check --json --strict --frame-check --caption-zone "x0=0;y0=.65;x1=1;y1=1;severity=error"
5. npx --yes hyperframes@0.9.2 render -o raw.mp4 --workers 1
6. ffmpeg -i raw.mp4 -map_metadata -1 -c copy "$1"
7. node gates.mjs "$1", plus an earlier render if one is given
Then write gates.mjs to check the file: 1080x1920, 30 fps, 801 frames; no black stretch and nothing frozen
for a second; no magenta pixel at the start, at each stop and at the end; Earth at its true size on the
first stops; every tick on its frame; loudness; the clock and words read back with a text reader at every
stop. Given an earlier render, every frame must match it. Run it and fix what fails.
Render twice: bash render.sh a.mp4, then bash render.sh film.mp4 a.mp4. The second run also proves the film comes out the same every time. The render's own "done" proves nothing. The gates do.
7. Look, then post
- Look at full-size frames: the first, each stop, the hit, the last.
npx --yes hyperframes@0.9.2 snapshot --frames 10writes stills to look at. - Watch it on a phone with the sound off. Can you read every word?
- Make the cover from a clean still of the best moment, labels hidden, with a 3 to 5 word hook.
- Put the credit line in the description. Ours for this film: "Moon: NASA's Scientific Visualization Studio (CGI Moon Kit). Earth: NASA Earth Observatory (Blue Marble: Next Generation, Black Marble). Numbers: NASA fact sheets."
- Check the caption's numbers too. Every number on our screen once passed while the draft caption had one wrong.
- Next film: copy this folder and change its data. Leave the finished one as it is.
The rules that matter
Twelve rules, each one from a mistake we made.
- A moving thing's trail is always behind it. Our light moved up with its tail pointing the same way, and it looked like it was going backwards.
- Words and numbers sit on their own solid band. Planets slid over our clock and its words.
- A counter lands exactly on the true value and never shows more than is left. Rounded to hundreds, our "KM TO GO" read 0 one frame before the hit, with 82 km still to go. Now its step shrinks near the end.
- A label says only what is true at that moment. Ours kept saying "Just reached Mars" long after Mars.
- The payoff answers the title's own question. Our title asked "how long", and our first answer gave a speed. The speed moved to the chip.
- Set line breaks by hand. Our title wrapped and left "SUN." alone on a line.
- New text is already showing on its own first frame. A fade that started fully clear left the frame a beat landed on with no words.
- Every file lives in the film's folder. An early card of ours loaded its animation library and a font from the web. A film must render the same twice.
- Next to a giant, open close on the small body, then pull back. At its true size beside the Sun, Earth was a 7-pixel dot among the stars.
- The subject never leaves the frame. A camera turn toward the Sun took Earth out of the picture for 4 seconds. Once it is too small to see, give it a marker ring and keep that ring to the payoff.
- A held ending keeps moving. A still ending reads as frozen. After the hit we pull back to something whole that keeps moving.
- Mix for a phone speaker. Our first bed sat at 55 Hz, which phone speakers barely play. Keep the bed above about 110 Hz, and every tick and the hit at least 3 dB over it, measured in a phone's band.
The checks before posting
Our render.sh stops at the first failure. In order:
- Numbers:
build.mjsasserts every value before anything renders. - Code: no random values, clocks, timers or web addresses in the scene.
- HyperFrames:
lintwith 0 errors and 0 warnings, thencheck --strict: layout, contrast, motion, and no text in the bottom 35% of the frame. - The file: 1080 x 1920, 30 fps, exactly 801 frames, the renderer's tags stripped.
- The picture: no black stretch, nothing frozen for a second, the ending still moving, not one magenta pixel at the start, the stops or the end.
- Sizes: Earth measured at its true size on the frames that matter.
- Sound: every tick within one frame of its stop, a silent gap before the hit, loudness -16 to -14 LUFS, true peak at most -1.5 dB.
- Read-back: a text reader reads the clock and the words off the video at every stop. Compare the digits: a reader can misread small unit letters next to big digits.
- Twice: a second render matches the first, frame for frame.
- You: full-size frames, a phone with the sound off, and your ears.
Honest limits
- Claude can't hear, and no check judges a look. Our checks once all passed on a muddy brown picture with dark planets you couldn't see. You approve the sound and the picture yourself.
- It draws what code and maps can show: bodies, light, numbers, words. It won't invent people or footage for you.
- Simplified, and it says so. Our fall leaves out the Sun's pull, air and the bodies' shapes, so the caption says "would". The clock is sped up, and the screen says "EARTH TO SCALE, CLOCK SPED UP." Say on screen what isn't to scale.
- Close-ups are hard. Near the end, a global Earth map turned to grey mush. We needed NASA's full-resolution tile of the coast we end over. Open ocean is flat colour at any resolution.
- Some free music isn't claim-free. Some Pixabay tracks are marked "Content ID Registered". Pixabay says a claim isn't a strike, but you can't earn from that video unless it's cleared. Read a track's own page first.
- Maps aren't photos. Solar System Scope fills gaps in its maps with made-up terrain. Credit every map, and never suggest NASA made your film.
- Render times, measured on our computer: about 40 s for a 22-second vertical, and 45 to 75 s for our 26.7-second films. Long videos add up: at our measured rate, 40 minutes of 3D shots is about 1.5 hours of rendering.
- The checks are per film. We write them for each film; a shared set is still on our list. A new film needs its gates adapted.
- Expect rounds. Three of our mistakes were caught only by looking at frames after every check had passed.
The full kit (4 film templates, all the rules, the checks) is coming soon. Follow @quietcosmos.studio.
Want an animated explainer for your page? DM us.