Spaces:
Running
Running
Download index.html from victor/ascii-odyssey-making-of: direct link, hf CLI and curl.
- Browser
- Download file 12.3 kB
-
https://huggingface.co/spaces/victor/ascii-odyssey-making-of/resolve/main/index.html
- Command line
-
hf download hf://spaces/victor/ascii-odyssey-making-of/index.html
-
curl -L -o index.html https://huggingface.co/spaces/victor/ascii-odyssey-making-of/resolve/main/index.html
12.3 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| <title>The Making of THE ODYSSEY (ASCII)</title> | |
| <style> | |
| :root{ --ink:#e8e6df; --bg:#0b0c0f; --card:#141519; --line:#26282e; --gold:#c9a24b; --mut:#8b8d95; } | |
| *{box-sizing:border-box} | |
| body{margin:0;background:var(--bg);color:var(--ink);font-family:-apple-system,system-ui,"Helvetica Neue",sans-serif;line-height:1.5} | |
| .wrap{max-width:1040px;margin:0 auto;padding:0 20px} | |
| header.hero{padding:90px 20px 50px;text-align:center;background:radial-gradient(80% 60% at 50% 0%, #1a1d24, var(--bg));} | |
| header.hero h1{font-size:clamp(30px,5.5vw,58px);margin:0 0 10px;letter-spacing:.01em} | |
| header.hero .sub{color:var(--gold);letter-spacing:.28em;text-transform:uppercase;font-size:12px;font-weight:600} | |
| header.hero p{color:var(--mut);max-width:640px;margin:22px auto 0;font-size:16px} | |
| .stack{display:flex;flex-direction:column;gap:64px;padding:10px 0 90px} | |
| .frame{border:1px solid var(--line);border-radius:8px;overflow:hidden;background:#000} | |
| .frame img,.frame video{display:block;width:100%;height:auto} | |
| h2{font-size:22px;margin:0 0 4px;display:flex;align-items:baseline;gap:10px} | |
| h2 .n{color:var(--gold);font-size:14px;font-weight:700;letter-spacing:.1em} | |
| .lede{color:var(--mut);margin:0 0 18px;font-size:15px;max-width:640px} | |
| .stat-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:14px;margin:26px 0} | |
| .stat{background:var(--card);border:1px solid var(--line);border-radius:8px;padding:16px 14px;text-align:center} | |
| .stat b{display:block;font-size:26px;color:var(--gold)} | |
| .stat span{font-size:12px;color:var(--mut);text-transform:uppercase;letter-spacing:.06em} | |
| .quote{border-left:3px solid var(--gold);padding:2px 0 2px 16px;margin:16px 0;color:#cfd0d6;font-size:14.5px} | |
| .quote::before{content:"β ";color:var(--gold)} | |
| .quote small{display:block;margin-top:4px;color:var(--mut);font-size:11.5px;letter-spacing:.04em;text-transform:uppercase} | |
| .gallery{display:grid;grid-template-columns:repeat(3,1fr);gap:10px} | |
| .gallery figure{margin:0;border:1px solid var(--line);border-radius:6px;overflow:hidden;background:#000} | |
| .gallery img{display:block;width:100%;aspect-ratio:16/9;object-fit:cover} | |
| .gallery figcaption{padding:8px 10px;font-size:12px;color:var(--mut)} | |
| .two-col{display:grid;grid-template-columns:1fr 1fr;gap:16px} | |
| .pill{display:inline-block;background:var(--card);border:1px solid var(--line);border-radius:20px;padding:4px 12px;font-size:12px;color:var(--gold);margin:2px 6px 2px 0} | |
| .fx-table{width:100%;border-collapse:collapse;font-size:13.5px;margin-top:14px} | |
| .fx-table td{padding:7px 8px;border-top:1px solid var(--line);color:#cfd0d6} | |
| .fx-table td:first-child{color:var(--gold);white-space:nowrap;font-weight:600} | |
| .fx-table td:nth-child(2){color:var(--mut);white-space:nowrap} | |
| footer{text-align:center;color:var(--mut);font-size:12px;padding:40px 20px 60px;border-top:1px solid var(--line)} | |
| footer a{color:var(--gold);text-decoration:none} | |
| @media(max-width:700px){ .gallery{grid-template-columns:repeat(2,1fr)} .two-col{grid-template-columns:1fr} } | |
| </style> | |
| </head> | |
| <body> | |
| <header class="hero"> | |
| <div class="sub">Behind the scenes</div> | |
| <h1>The Making of<br/><em>THE ODYSSEY</em></h1> | |
| <p>A 90-second film, cast entirely in ASCII text, built and directed end-to-end by <b>Kimi K3</b> running inside Claude Code β no stock footage, no templates, no human touching a single pixel. Here's how it actually happened.</p> | |
| </header> | |
| <div class="wrap stack"> | |
| <section> | |
| <div class="frame"><img src="img/title.png" alt="Title card"/></div> | |
| </section> | |
| <section> | |
| <h2><span class="n">01</span> One paragraph, one film</h2> | |
| <p class="lede">The entire brief was a single sentence: build the most realistic Boeing 747 in three.js β wait, wrong file. For this one, the ask was even shorter: <em>write an ASCII adaptation of Homer's Odyssey and direct it like a real film.</em> Everything below β the renderer, the ten scenes, the camera moves, the score β Kimi invented on its own.</p> | |
| <div class="stat-row"> | |
| <div class="stat"><b>0</b><span>assets used</span></div> | |
| <div class="stat"><b>12</b><span>scenes</span></div> | |
| <div class="stat"><b>2,172</b><span>frames hand-drawn</span></div> | |
| <div class="stat"><b>90.5s</b><span>runtime</span></div> | |
| </div> | |
| </section> | |
| <section> | |
| <h2><span class="n">02</span> It built its own camera before it built the story</h2> | |
| <p class="lede">Before writing a single scene, Kimi wrote itself a tiny film studio: a character-grid renderer, a rasterizer to turn text into pixels, and β critically β a way to look at what it had drawn. Every scene got checked against a real render before moving on.</p> | |
| <div class="quote">I'll build this as a real production: a Python ASCII render engine, scene-by-scene direction, a Pillow rasterizer, visual self-inspection loops, then ffmpeg encode.<small>Kimi K3, turn 1</small></div> | |
| </section> | |
| <section> | |
| <h2><span class="n">03</span> The blooper reel it never showed you</h2> | |
| <p class="lede">This is the fun part. Kimi rendered a frame, looked at it, hated it, and fixed it β over and over. Real quotes, no cleanup:</p> | |
| <div class="two-col"> | |
| <div> | |
| <div class="quote">The title text is uniform blobs β no internal textureβ¦ <small>after render #1</small></div> | |
| <div class="quote">Title still fails β the vignette multiplies the text down to mid-density glyphs. I need to draw text <em>after</em> vignetting.<small>after render #2</small></div> | |
| <div class="quote">My "silhouette + rim" double-draw in Cyclops cancels itself (the max undoes the min).<small>debugging the cave scene</small></div> | |
| </div> | |
| <div> | |
| <div class="quote">Storm is a wall of dense glyphs that swallows the ship β the sea needs a gamma to suppress mid-tones, plus a hull shadow to silhouette the ship against foam.<small>fixing the storm</small></div> | |
| <div class="quote">Sea and moon now work. Remaining fixes: title text needs near-uniform dense glyphs, stars should be tiny dots not bright lettersβ¦<small>the punch-list kept growing</small></div> | |
| <div class="quote">Common diseases: backgrounds are dot-fields instead of black, vignette dims the text⦠time for a comprehensive revision pass.<small>the moment it stopped patching and rewrote</small></div> | |
| </div> | |
| </div> | |
| </section> | |
| <section> | |
| <h2><span class="n">04</span> Old tricks, done in a text file</h2> | |
| <p class="lede">No shaders, no GPU β every "cinematic" effect is arithmetic on a grid of characters.</p> | |
| <div style="margin:10px 0 4px"> | |
| <span class="pill">film grain β random brightness jitter, Β±3%</span> | |
| <span class="pill">vignette β darken toward the frame edge</span> | |
| <span class="pill">bloom on fire & torches</span> | |
| <span class="pill">gate weave β vintage-projector wobble</span> | |
| <span class="pill">dither, to kill banding in the glows</span> | |
| </div> | |
| <p class="lede" style="margin-top:14px">(The gate weave got cut in the final pass β at character-grid resolution a one-row shift is a visible jump, not a wobble, so it was removed for a steadier image. Real film-restoration problem, solved the same way an editor would.)</p> | |
| </section> | |
| <section> | |
| <h2><span class="n">05</span> The gallery</h2> | |
| <p class="lede">All twelve scenes, straight off the render engine.</p> | |
| <div class="gallery"> | |
| <figure><img src="img/troy.png"/><figcaption>Troy β the fall and the departure</figcaption></figure> | |
| <figure><img src="img/storm.png"/><figcaption>Poseidon's storm</figcaption></figure> | |
| <figure><img src="img/cyclops.png"/><figcaption>The cave of Polyphemus</figcaption></figure> | |
| <figure><img src="img/sirens.png"/><figcaption>The Sirens' song</figcaption></figure> | |
| <figure><img src="img/charybdis.png"/><figcaption>Charybdis, the whirlpool</figcaption></figure> | |
| <figure><img src="img/calypso.png"/><figcaption>Calypso's isle</figcaption></figure> | |
| <figure><img src="img/ithaca_card.png"/><figcaption>Ithaca β the twentieth year</figcaption></figure> | |
| <figure><img src="img/hall.png"/><figcaption>The great hall</figcaption></figure> | |
| <figure><img src="img/penelope.png"/><figcaption>The reunion</figcaption></figure> | |
| <figure><img src="img/end.png"/><figcaption>Journey's end</figcaption></figure> | |
| <figure><img src="img/cyc_card.png"/><figcaption>Intertitle, silent-film style</figcaption></figure> | |
| <figure><img src="img/title.png"/><figcaption>Opening card</figcaption></figure> | |
| </div> | |
| </section> | |
| <section> | |
| <h2><span class="n">06</span> Beaten 495 times, kept working</h2> | |
| <p class="lede">Kimi K3 runs on a shared, rate-limited API. Over the two-hour build it got knocked back with "rate limit exceeded" <b>495 separate times</b> β and just retried, quietly, until each scene actually rendered.</p> | |
| <div class="stat-row"> | |
| <div class="stat"><b>145</b><span>agent turns</span></div> | |
| <div class="stat"><b>495</b><span>rate-limit hits survived</span></div> | |
| <div class="stat"><b>125 min</b><span>build time</span></div> | |
| <div class="stat"><b>$6.75</b><span>total cost</span></div> | |
| </div> | |
| </section> | |
| <section> | |
| <div class="frame"> | |
| <video controls preload="metadata" poster="img/title.png"> | |
| <source src="odyssey_scored.mp4" type="video/mp4" /> | |
| </video> | |
| </div> | |
| </section> | |
| <section> | |
| <h2><span class="n">07</span> Then it scored its own film</h2> | |
| <p class="lede">In a follow-up session, Kimi turned the silent film into a full production β narrator, orchestral score, and sound design β using ElevenLabs. It wrote twelve original passages, one per scene, then had to solve the hardest part: making a real human voice land <em>exactly</em> on the picture.</p> | |
| <div class="quote">Bill speaks ~20% slower than George β every passage was shortened and re-measured until it fit its window with margin: troy 28β19 words, cyclops 28β22 (kept the "Nobody" wordplay), penelope 24β16.<small>repacing the narration, take 2</small></div> | |
| <p class="lede">That "Nobody" line is a three-thousand-year-old joke: in the original, Odysseus tells the Cyclops his name is <em>Nobody</em> β so when he's blinded and screams for help, the other Cyclopes hear "Nobody is hurting me!" and walk away. Kimi kept the gag.</p> | |
| </section> | |
| <section> | |
| <h2><span class="n">08</span> Casting an elder bard</h2> | |
| <p class="lede">The first cut used a smooth, youthful narrator. Asked for someone older, Kimi auditioned voices instead of guessing:</p> | |
| <div class="quote">The only roster voice labeled "old" β wise, mature, balanced. Previewed against Callum (husky trickster β wrong character) with the title line before committing.<small>casting the narrator</small></div> | |
| </section> | |
| <section> | |
| <h2><span class="n">09</span> The trick that keeps the voice audible</h2> | |
| <p class="lede">Orchestral score and narration fight for the same space. Kimi's fix is the same one real film mixers use: <b>sidechain ducking</b> β the score automatically drops in volume the instant the narrator speaks, then swells back in the silence.</p> | |
| <table class="fx-table"> | |
| <tr><td>Sidechain duck</td><td>8:1, 30ms attack</td><td>music ducks 10β13 dB under speech, swells back between lines</td></tr> | |
| <tr><td>Thunder crack</td><td>18.5s</td><td>timed to the lightning flash, not just "somewhere in the storm"</td></tr> | |
| <tr><td>Cyclops' roar</td><td>30.6s</td><td>lands exactly on the blinding flash</td></tr> | |
| <tr><td>Arrow whoosh</td><td>68.8s</td><td>fired on the frame the bowstring releases</td></tr> | |
| <tr><td>Hall melee</td><td>70.3s</td><td>covers the suitors' fall under score</td></tr> | |
| </table> | |
| <p class="lede" style="margin-top:16px">Final master: three audio buses (voice / ducked music / effects) through a limiter and two-pass loudness normalization β the same signal chain a professional mix engineer would reach for.</p> | |
| </section> | |
| <footer> | |
| Built end-to-end by <b>Kimi K3</b> (via OpenRouter) inside Claude Code, on the user's own Hetzner instance. | |
| ASCII engine, ten scenes, narration, score and sound design β all model-authored. | |
| </footer> | |
| </div> | |
| </body> | |
| </html> | |