운수 좋은 날

현진건, 1924 — 코드로 렌더한 4분 38초짜리 절차적 단편 영화에 대하여

▶ 영화 보기 4분 38초 · 16 시퀀스 · 65 샷 · 외부 에셋 0개 · 브라우저에서 바로 재생

01이게 뭔가

2026년 8월 2일, Andrej Karpathy가 실험 하나를 올렸다. Claude Opus 5에게 반지의 제왕 첫 문단과 100만 토큰 예산(약 $10)을 주고 "이걸 three.js로 렌더링해봐"라고만 시킨 것이다. Opus는 약 2시간 동안 혼자 돌면서 코드를 썼고, 92.6초짜리 절차적 단편 영화가 나왔다. 외부 에셋은 하나도 쓰지 않았다. 나무도 집도 인물도 전부 코드가 그 자리에서 만들어낸 폴리곤이다.

그가 이 실험에서 흥미로워한 건 결과물의 완성도가 아니었다. "제정신인 사람이라면 아무도 이렇게 커스텀한 걸 만드느라 시간을 쓰지 않을 텐데, LLM은 세상의 모든 끈기와 인내심을 갖고 있다"는 것 — "아무도 안 할 일"이 "$10이면 하지 뭐"로 바뀌는 지점이었다.

우리는 같은 실험을 한국 소설로 했다. 현진건의 「운수 좋은 날」(1924). 호비튼의 언덕 대신 1924년 비 내리는 경성, 빌보 대신 인력거꾼 김 첨지다. 결과물은 4분 38초, 16 시퀀스, 65 샷. 캡션 53줄은 전부 원문 그대로이고, 의존성은 여전히 three.js 하나, 외부 에셋은 여전히 0개다.

Karpathy의 원문과 답글에서만 드러난 사실들은 별도 페이지에 따로 정리했다.

02원작

전문은 한글 7,035음절이다. 낭독 속도 실측 5.41음절/초로 그대로 읽으면 29분이 나오고, 한 번의 작업 런에 들어가지 않는다. 그래서 전문의 15%인 1,097음절만 발췌했다. 4분 38초는 그 15%가 만들어낸 길이다.

다만 캡션은 한 글자도 고치지 않았다. 발췌란 문장을 건너뛰는 것이지 문장을 줄이는 게 아니다. 요약도, 현대어역도, 의역도 없다. 고른 문장은 1924년 표기 그대로 간다 — "내리었다", "첨지", "모주". 건너뛴 구간은 캡션 없는 무음 샷으로 넘긴다. 침묵도 연출이다.

도입부만 자르지 않은 이유는 분명하다. 「운수 좋은 날」은 마지막 대사가 전부인 소설이다. Karpathy처럼 첫 문단만 렌더했다면 비 오는 거리 스케치로 끝났을 것이고, 이 작품을 고른 의미가 사라진다. 그래서 7개 비트 — 비 오는 아침, 연달아 붙는 손님, 아픈 아내와 설렁탕, 인력거 질주, 선술집, 불안, 귀가 — 를 모두 통과하도록 문장을 골랐다.

설렁탕을 사다놓았는데 왜 먹지를 못하니, 왜 먹지를 못하니... 괴상하게도 오늘은! 운수가 좋더니만...

— 마지막 캡션. 원문 그대로.

03어떻게 만들었나

  1. PASS 1 — CLAUDE OPUS 대본과 컷 리스트 코드는 한 줄도 쓰지 않는다. 원문에서 문장을 고르고, 65개 샷으로 쪼개고, 샷마다 연출 메모를 남긴다. 산출물은 narration.jsonlshots.json 둘뿐.
  2. TTS — FISH AUDIO 나레이션 53클립을 하나의 연속 스프라이트로 캡션 한 줄에 클립 하나. 실측 길이를 재서 277.6초짜리 통짜 mp3와 큐 시트를 만든다. 개별 문장을 잘라 큐잉하지 않는 건 낭독의 이음매를 만들지 않기 위해서다.
  3. RETIME 측정된 오디오 길이로 샷 길이를 확정 각 샷의 실제 길이와 캡션 슬라이스를 timing.js로 굽는다. 이 파일이 PASS 2의 정답지가 된다.
  4. PASS 2 — CLAUDE OPUS three.js 영화 전체 엔진, 절차적 에셋 뱅크, 16개 시퀀스의 연출 코드를 전부 쓴다. 샷은 연출상 자연스러운 길이로 짜고, warp = authored / real로 재생 속도만 스케일해 실측 길이에 맞춘다. 애니메이션 곡선의 모양은 그대로 두고 속도만 바뀌므로 동작이 깨지지 않는다.
  5. VERIFY 스크린샷 자가검증 시퀀스를 하나 끝낼 때마다 헤드리스 크롬 + SwiftShader로 실제 WebGL을 렌더해 샷마다 한 장씩 찍고, 콘솔 에러를 모은다. 에러가 하나라도 있으면 종료코드 1이 나온다.

Karpathy와 뒤집은 것 — audio-first

Karpathy는 그림을 먼저 만들고, ElevenLabs 나레이션을 나중에 붙인 뒤, retime-to-narration.mjs로 샷 길이를 다시 맞췄다. 우리는 그 순서를 뒤집었다. TTS를 먼저 만들어 길이를 못 박고, 그 길이에 그림을 맞춘다.

이유는 단순하다. 길이를 먼저 확정하면 모델이 두 시간 내내 "이 샷 몇 초짜리지"를 추측하지 않아도 된다. 추측이 사라진 만큼 토큰이 실제 지오메트리로 간다. 대신 잃는 것도 있다 — "한 줄 프롬프트로 어디까지 가나"라는 원래 실험의 순수성이다.

다섯 번째 단계가 필요한 이유는 Karpathy가 트윗의 마지막 문단에서 짚은 것과 같다. LLM은 영상을 네이티브하게 지각하지 못해서 자기가 만든 화면을 감사할 수 없다. 스크린샷은 그 구멍을 메우는 임시방편이지, 해결책이 아니다.

04사용 모델과 비용

이 숫자는 추정이다. 아래 토큰 수는 각 작업을 맡은 서브에이전트가 보고한 총 토큰이며 input과 output이 구분되어 있지 않다. input과 output의 단가는 5배 차이가 나기 때문에, 총 토큰만으로는 비용을 하나의 값으로 확정할 수 없다. 그래서 전부 input일 때(하한)전부 output일 때(상한)를 함께 표시한다. 실제 비용은 그 사이 어딘가에 있고, 보통은 하한에 가깝다. Karpathy의 "$10"과 나란히 놓을 숫자이므로 이 불확실성을 감추지 않는다.
단계별 토큰 사용량
단계모델토큰 하한
전부 input
상한
전부 output
불러오는 중…

Karpathy와 나란히

불러오는 중…

Karpathy의 값은 그의 트윗과 공개 소스에서 온 것이다. 코드 줄 수의 5,500 / 6,933 차이는 트윗의 서술과 실제 소스를 직접 센 값의 차이다.

05Karpathy 원문

트윗 전문과 번역, 지표, 그리고 답글에서만 나온 사실들 — 오디오는 본인이 수동으로 만들었다는 것, three.js 공식계정의 InstancedMesh 지적을 다시 Opus에 먹였다는 것, 절차적 코드로 스토리보드를 잡고 video-to-video로 텍스처링하자는 후속 구상 — 을 별도 페이지에 정리했다.

  1. ① 원샷 평가("펠리컨 SVG")는 수명이 끝나간다 → 장기 에이전트 작업으로 이동
  2. ② LLM은 무한한 끈기를 가진다 → "아무도 안 할 일"이 "$10이면 하지"가 된다
  3. ③ 초-커스텀 일회용 세계 — ephemeral GTA of X on demand
  4. ④ 검증이 병목 — LLM은 영상을 지각하지도, 게임을 플레이하지도 못한다

→ Karpathy 원문 전체 읽기

06기술

07크레딧

▶ 영화 보기 여기까지 읽었다면, 이제 보실 차례입니다