all writing

Hướng dẫn làm motion video bằng Claude Opus 5.5

· claude· ai· motion-design

Nguồn: Bài này dịch và diễn giải lại từ How to build motion design studio with Opus 5.5 (Full-course) của Movez (@0xMovez), đăng ngày 27/09/2026. Code và prompt template là của tác giả. Danh sách nguồn đầy đủ ở cuối bài.

Sau khi Claude Opus 5.5 ra mắt ngày 22/09/2026, chỉ trong vài giờ trên X đã tràn ngập showreel, video launch sản phẩm, MV ca nhạc, thậm chí phim lịch sử dài 5 phút. Tất cả đều là motion design (đồ họa chuyển động) được render từ code. Caption thì ghi "chỉ một prompt", còn bình luận thì than "dân motion designer hết đất sống".

Cả hai đều chỉ đúng một nửa. Có clip đúng là ra từ một prompt khoảng 30 từ. Nhưng có clip đi kèm một bản yêu cầu (brief) dài 9.500 ký tự, cả một thư mục skill (gói hướng dẫn cài thêm cho Claude), hai API key và 12 tiếng để Claude tự chạy. Như Thariq (team Claude Code) nhận xét: "the post: 'Claude one-shot this' - the prompt: 10k characters with good takes plus skills, examples and API keys" (bài đăng thì khoe "Claude làm một phát ăn ngay", còn prompt thật thì dài 10 nghìn ký tự, kèm skill, ví dụ và API key).

Bài gốc xếp các video trong trào lưu thành 4 cấp độ, theo số liệu chính các tác giả công bố. Prompt càng dài thì Claude chạy càng lâu:

Cấp độ

Độ dài prompt

Thời gian chạy

Trong bài

L1 · Prompt một dòng

~150 ký tự

15–50 phút

Mục 3

L2 · Video cho thương hiệu

~350 ký tự

30–45 phút

Mục 7.1

L3 · Spec danh sách trạng thái

1.500–3.000 ký tự

~1–2 tiếng, tính cả sửa

Mục 7.3

L4 · Brief đạo diễn

9.500–19.000 ký tự

6–12 tiếng tự chạy

Mục 8.3

Bốn cấp độ: độ dài prompt và thời gian chạy tăng cùng nhau, từ prompt một dòng tới brief đạo diễn

Còn phần lớn người tự thử thì nhận về cùng một kiểu video: chữ căn giữa trên nền gradient, mọi thứ hiện dần lên, cuối video là logo. Lý do: họ không đưa Claude mẫu tham khảo, không cho nó một engine để render, và không bắt nó tự xem lại khung hình của mình.

Ý chính của bài gốc gói trong một câu: prompt chỉ chiếm 10% chất lượng video, 90% còn lại nằm ở "harness", tức bộ khung làm việc bao quanh Claude: công cụ render, file luật, âm thanh và vòng tự kiểm tra. Bài gốc là một khóa 12 bước chia 4 phần. Bản tiếng Việt này giữ đủ nội dung nhưng sắp xếp lại theo thứ tự dễ làm theo hơn cho người mới, kèm giải thích thuật ngữ.

Cách đọc code trong bài: khối code có nhãn 🤖 là phần Claude tự viết hoặc tự chạy, bạn chỉ cần đọc để hiểu nó đang làm gì. Khối không có nhãn là phần bạn tự gõ hoặc dán: lệnh cài đặt, file CLAUDE.md, các prompt và skill.

Bạn sẽ làm được gì

  • Hiểu cách Claude biến code thành một file video MP4

  • Làm video đầu tiên chỉ với một câu prompt

  • Làm chuyển động trông tự nhiên, khớp hình với nhịp nhạc

  • Cho Claude tự xem lại và sửa video của chính nó

  • Viết prompt cho video sản phẩm, video dài, và đóng gói thành lệnh dùng lại được

  • Biến quy trình này thành một dịch vụ có thể bán

Yêu cầu

  • Claude Code (bản chạy trong terminal), dùng model Opus 5.5. Bản chat trên web không làm được, lý do ở mục 1.

  • Máy macOS hoặc Linux, có Node 22+, Python 3, ffmpeg.

  • Biết JavaScript cơ bản và dùng được terminal.

  • Chi phí: video 15 giây ở mức effort cao đã tốn một lượng token đáng kể. Để dễ hình dung: phim màu nước 45 giây của Mable Joseph dùng 62,7 triệu token (96% là đọc lại từ cache), khoảng $34 nếu tính theo giá API niêm yết, mất gần 7 tiếng. Nên thử với video ngắn trước.


1. Ý tưởng cốt lõi

Claude không tạo video, Claude viết chương trình vẽ ra video

Opus 5.5 nhận vào text và ảnh, nhưng chỉ trả ra text, không xuất được file MP4. Vì vậy mọi video trong trào lưu này đều là một chương trình do Claude viết, sau đó các công cụ khác chạy chương trình đó để ra video.

Hãy hình dung video là một tập giấy lật (flipbook). Claude không vẽ từng trang. Nó viết một hàm tên seek(t): đưa vào thời điểm t (tính bằng giây), hàm vẽ ra đúng trang ở thời điểm đó.

Điểm mấu chốt: hỏi cùng một giây thì luôn nhận về đúng một hình, bất kể máy nhanh hay chậm, chạy lần thứ mấy. Hình ở giây 4,2 chỉ phụ thuộc vào con số 4,2, không phụ thuộc vào đồng hồ hay những gì đã vẽ trước đó. Dân lập trình gọi tính chất này là tất định (deterministic).

Từ code thành MP4

Có hai công cụ tham gia:

  • Playwright: thư viện điều khiển trình duyệt Chrome bằng code, chạy ngầm không hiện cửa sổ (gọi là headless browser).

  • ffmpeg: công cụ dòng lệnh để cắt, ghép, chuyển đổi video và âm thanh.

Quy trình render:

index.html (chứa hàm seek(t), vẽ lên canvas)
   → Playwright mở trang, gọi seek(t) cho từng khung hình
     (video 15 giây × 60 khung hình/giây = 900 lần)
   → chụp mỗi khung hình thành ảnh PNG
   → ffmpeg ghép các ảnh thành file MP4

Tại sao phải làm vậy? Animation trên web thông thường chạy theo đồng hồ thật (setTimeout, requestAnimationFrame). Máy lag là hình giật, và mỗi lần quay màn hình lại ra một kết quả khác. Ở đây thời gian do chương trình render tự "tua" tới, nên lần render nào cũng giống hệt nhau. Muốn sửa một chi tiết, chỉ cần sửa code rồi render lại.

Vì sao bắt buộc dùng Claude Code

Bản chat trên web vẫn viết được code animation, nhưng chỉ Claude Code (hoặc agent nào có quyền chạy lệnh trên máy) mới tự render được video, tự cắt video thành ảnh để xem lại, tự đo nhịp nhạc, rồi tự sửa lỗi. Chính vòng kiểm tra này tạo ra khác biệt giữa lần thử đầu "hơi mid" (tầm thường) mà nhiều người than phiền và những video viral.

Lưu ý khi chọn công cụ

Tommy Rossi chạy thử và mổ xẻ code Opus viết ra. Khi không được chỉ định, Opus chọn đúng cách trên (bài gốc gọi là route A): một file index.html, không cài thư viện ngoài, hàm seek được gọi qua eval, Playwright chụp từng khung hình, ffmpeg ghép thành MP4. Phần nhạc thì Opus dùng Python để đo mốc thời gian của các beat, rồi chép thẳng các con số đó vào file HTML.

Opus bỏ qua các framework làm video bằng code như Remotion hay HyperFrames (bài gốc gọi là route B, xem mục 8.4), kể cả khi máy đã cài sẵn. Muốn dùng framework thì phải nói rõ trong prompt.

Thật ra bài gốc vẽ ra tới 4 con đường từ brief tới file MP4:

Bốn con đường từ brief tới MP4: vẽ bằng code, dùng framework, kết hợp AI tạo ảnh và video, hoặc dựng từ cảnh quay thật
  • A · Vẽ bằng code: Canvas, SVG hoặc p5.brush, render bằng Chrome chạy ngầm. Hợp với showreel, UI motion, video lặp, pixel art. Điểm mạnh: không cần cài thêm gì, sửa được mọi chi tiết, và là cách Opus tự chọn. Điểm yếu: nhân vật và cảnh chân thực cần spec rất chi tiết.

  • B · Framework: Remotion (React) hoặc HyperFrames (HTML). Hợp với video giải thích, video sản phẩm, làm series. Điểm mạnh: có giao diện xem trước, dùng lại được component, có skill hướng dẫn Claude viết code. Điểm yếu: Remotion phải mua license công ty nếu team trên 3 người.

  • C · Kết hợp: AI tạo ảnh và video làm nền, Opus ghép và vẽ đè lên. Hợp với MV ca nhạc, phim có nhân vật. Điểm mạnh: chuyển động vật lý và khuôn mặt lấy từ AI video. Điểm yếu: tốn tiền API, phải canh khớp, kém tất định hơn.

  • D · Dựng từ cảnh quay: cắt ghép clip của bạn, thêm phụ đề, B-roll, PiP. Hợp với video nói trước camera, reels, video launch. Điểm mạnh: giữ nguyên khuôn mặt và giọng thật của bạn. Điểm yếu: cần cảnh quay gốc và âm thanh sạch.

(B-roll: cảnh phụ chèn xen vào cảnh chính; PiP: khung hình nhỏ đè lên khung hình lớn)

Bài này tập trung vào route A (mục 4). Route B ở mục 8.4, còn route C chính là kỹ thuật generate-then-trace của Donald ở mục 8.3.


2. Setup

2.1. Cài đặt bắt buộc

# macOS (Linux: cài node, ffmpeg, python3 qua trình quản lý gói của distro)
brew install node ffmpeg python

# Tạo project và cài Playwright + trình duyệt Chromium
mkdir motion-studio && cd motion-studio && npm init -y
npm i -D playwright && npx playwright install chromium

2.2. Cài đặt tùy chọn

Chỉ cài khi cần dùng tới:

# Để đo nhịp từ file nhạc có sẵn (mục 5.2)
pip install numpy librosa soundfile

# Để dùng framework Remotion hoặc HyperFrames (mục 8.4)
npx skills add remotion-dev/skills
npx skills add heygen-com/hyperframes

# Để làm video phong cách vẽ tay
claude plugin marketplace add buildwithhanif/claude-animation-skill
claude plugin install claude-animation@claude-animation-skill

2.3. Cấu trúc thư mục

Đây là toàn bộ các file sẽ xuất hiện trong bài. Bạn chưa cần tạo hết ngay, mỗi file sẽ được giới thiệu ở mục tương ứng.

motion-studio/
├── CLAUDE.md          ← bạn tạo (mục 2.5): luật chung cho Claude
├── index.html         ← Claude viết: chứa seek(t), vẽ video (mục 4.1)
├── render.mjs         ← Claude viết hoặc bạn copy: xuất MP4 (mục 4.2)
├── lib/motion.js      ← hàm spring cho chuyển động (mục 4.3)
├── beats.py           ← đo nhịp nhạc (mục 5.2)
├── sfx.mjs            ← tạo hiệu ứng âm thanh (mục 5.3)
├── .env               ← API key (nếu có)
├── audio/             ← file nhạc
├── refs/              ← video/ảnh mẫu để tham khảo
├── docs/              ← Claude ghi style guide, danh sách cảnh
├── prompts/           ← các prompt dùng lại (mục 6.2)
└── out/               ← video và ảnh xuất ra

2.4. Chọn mức effort

Effort là mức "suy nghĩ kỹ" của model. Mức càng cao thì Claude lập kế hoạch và tự kiểm tra càng nhiều, đổi lại chạy chậm hơn và tốn token hơn. Opus 5.5 mặc định chạy medium (và luôn suy nghĩ trước khi trả lời), còn mọi video "one-shot" viral mà bài gốc tổng hợp đều chạy xhigh hoặc max.

Chạy Claude Code:

claude --model claude-opus-5-5

Sau đó gõ /model trong Claude Code, chọn Opus 5.5 và chỉnh effort.

Việc

Effort

Sửa nhỏ, render lại

medium

Làm video mới

xhigh

Video mà 3 giây đầu phải "gánh" cả một buổi launch

max

2.5. Tạo file CLAUDE.md

Claude Code tự đọc file CLAUDE.md ở thư mục gốc project mỗi lần chạy. Đặt luật chung ở đây để không phải nhắc lại trong từng prompt.

Nội dung file gồm 4 phần:

Phần

Ý nghĩa

Render contract

Mỗi khung hình chỉ phụ thuộc vào thời điểm t: cùng giây thì luôn ra cùng hình. Cấm random, cấm timer. Xuất MP4 chất lượng cao

Look

Cấm các style "nhìn là biết AI": tiêu đề căn giữa trên nền gradient, mọi thứ cùng hiện dần (fade in), khung viền, nhãn ở góc

Sound

Âm thanh tự tạo bằng code, khớp nhịp nhạc, âm lượng chuẩn mạng xã hội

Loop

Trước khi cho bạn xem: tự chụp ảnh, tự chấm điểm, sửa tới khi mọi tiêu chí đạt 8/10

Bạn chưa cần hiểu hết từng dòng, các thuật ngữ sẽ được giải thích ở những mục sau. Tạo motion-studio/CLAUDE.md:

# Motion studio rules

## Render contract
- Every film is a pure function of time: `window.seek(t)` paints frame t.
- No CSS transitions, no setTimeout, no requestAnimationFrame in render mode,
  no state carried between frames. Seeded noise only (mulberry32), never Math.random.
- Render with `node render.mjs`, encode H.264 yuv420p, CRF 16.

## Look
- Banned defaults: centered title on gradient, everything fading in,
  corner labels and frame borders, glow on UI chrome, generic particle bursts.
- One display face, one UI face. One accent color unless the brief says otherwise.
- Every 2 to 4 seconds something new must happen on screen.

## Sound
- Score and SFX are synthesized in code unless a track is supplied.
- Place hits on the measured beat grid (beats.json). Loudness -14 LUFS.

## Loop before you show me anything
1. Render one frame per beat as a contact sheet and LOOK at it.
2. Score it 1-10 on: hook in first 2s, readability at phone size,
   motion quality, variety, brand accuracy, sound sync.
3. Fix the 3 worst problems. Repeat until every score is 8+.
4. Only then do the full render.

3. Làm video đầu tiên

Mục này đi trọn một vòng từ prompt tới file MP4. Các mục sau sẽ giải thích chi tiết từng phần.

Bước 1: Mở Claude Code

cd motion-studio
claude --model claude-opus-5-5

Gõ /model và đặt effort xhigh.

Bước 2: Dán prompt

Trong 9 bài đăng định hình trào lưu mà bài gốc tổng hợp, 4 bài dùng đúng câu prompt này. Nó xuất phát từ showreel 15 giây của Stephan Livera (effort max). Rob Hallam chạy lại y nguyên ở effort xhigh và viết "tôi không tin nổi, nhưng đây thật sự là one-shot", ra luôn cả âm thanh. Nghĩa là xhigh là đủ, và phần tiếng không phải ghép thêm sau. Himanshu cũng chạy câu này ở max (nhớ bật tiếng khi xem).

make a dynamic 15-second motion graphics video that shows what an
incredible motion designer you are, like it's your showreel for a résumé.
go all out.

Vì sao chỉ một câu mà ra được 15 giây có chuyển động camera, chữ động và cả âm thanh:

  • "showreel for a résumé": showreel là video ngắn dân motion dùng để khoe tay nghề, giống portfolio dạng video. Thể loại này có sẵn luật chơi (cắt cảnh nhanh, mỗi cảnh một kỹ thuật, cảnh đẹp nhất đặt lên đầu), Claude đã biết một showreel trông thế nào.

  • "what an incredible motion designer you are": video nói về chính Claude, nên nó không cần hiểu sản phẩm hay thông điệp nào của bạn. Nó chỉ cần khoe kỹ thuật. Không có nội dung nào để làm sai.

  • "15-second": đủ ngắn để làm xong trong một lượt, đủ dài cho 6–8 cảnh.

  • "go all out" ("chơi hết mình"): đóng vai trò như hệ số nhân, đẩy Claude làm công phu hơn nữa trên nền effort xhigh hoặc max. Nó không thay được việc đặt effort ở bước 1.

Giải phẫu câu prompt showreel: độ dài 15 giây, chủ đề là chính Claude, thể loại showreel, và câu "go all out" nhân thêm effort

Bước 3: Chờ Claude làm việc

Claude sẽ tự viết index.html (chứa hàm seek(t)) và một script render (thường là render.mjs, như quy định trong CLAUDE.md), sau đó tự render, tự xem lại và sửa.

Trong lúc chờ, bạn có thể mở index.html bằng trình duyệt để xem bản chạy thử.

Bước 4: Xem kết quả

Khi xong, video nằm trong thư mục out/. Nếu muốn tự render lại nhanh để xem:

node render.mjs --sub 1

Kết quả là out/silent.mp4 (chưa có tiếng, phần âm thanh xem mục 5).

Nhược điểm của prompt này

Vì hàng trăm người dùng đúng prompt này, video ra trông na ná nhau. Bộ dữ liệu tổng hợp của athemeroy gọi hiện tượng này là brief contagion ("lây prompt"). Chỉ nên dùng prompt này để kiểm tra setup đã chạy được chưa, rồi chuyển sang cách khác.

Các biến thể đã được dùng và cho kết quả tốt

Bài gốc gom 4 kiểu biến tấu từ câu prompt trên:

Biến thể

Lấy cảm hứng từ

Thêm gì

Dài hơn, đòi hỏi âm thanh

@kloss_xyz (showreel piano 90 giây)

Khung 16:9, 60 giây, nhạc piano tự sáng tác, mọi lần cắt cảnh khớp nhạc

Chặn "dấu hiệu AI"

@1littlecoder

Cấm khung viền và chữ ở góc, hai dấu hiệu dễ lộ video do AI làm

Kể chuyện thay vì khoe kỹ thuật

@sonnylazuardi

Kể lịch sử một chủ đề từ đầu tới nay, để Claude tự lên storyboard

Đóng vai agency

(bài gốc không ghi nguồn)

Showreel của một studio branding cho founder, mỗi cảnh một kỹ thuật

# Longer, with a sound bar (pattern from @kloss_xyz's 90-second piano reel)
make a dynamic 16:9, 60-second motion graphics showreel that shows your real creative limits.
S-tier sound design, no generic synth pads. Compose an original piano score and sync every
cut to it. Export 1080p MP4.

# Anti-slop guardrail (pattern from @1littlecoder)
make a dynamic 10-second motion graphics video that introduces who you are as Opus 5.5.
Avoid frames and text in the corners, the usual giveaways of AI-made video.

# Story instead of techniques (pattern from @sonnylazuardi)
use your showreel energy, but tell a story: the history of [TOPIC] from [START] to today,
surprise me with the storyboard. 45 seconds, vertical 9:16.

# Agency persona
make a 30-second showreel as if you were a niche branding studio for startup founders.
Create every graphic from scratch. One accent color. Every shot is a different technique.

(S-tier: hạng cao nhất; generic synth pads: lớp nhạc nền điện tử chung chung, nghe là biết "nhạc mặc định")

Nhưng nhớ câu này của tác giả: prompt một dòng chỉ kiểm tra được engine, không bao giờ kiểm tra được ý tưởng, vì trong nó không có ý tưởng nào. Để làm video thật, xem mục 7.


4. Bên trong engine render

Đây là route A (cách Opus tự chọn khi không được chỉ định) được viết lại cho gọn, gồm hai file: trang HTML vẽ được bất kỳ thời điểm nào khi được gọi, và script render "đi" dọc theo thời gian rồi đẩy từng khung hình sang ffmpeg.

Engine render: index.html có window.seek(t), render.mjs gọi từng thời điểm, ffmpeg gộp khung phụ thành 60 fps; bản không tiếng silent.mp4 ghép với nhạc score.wav thành bản cuối

Bạn không cần tự gõ phần này, vì Claude sẽ tự viết một phiên bản tương tự. Hãy đọc để hiểu Claude đang làm gì và biết sửa ở đâu khi cần. Muốn chủ động hơn, bạn có thể lưu các file này vào project làm khung sẵn, rồi bảo Claude dùng lại.

4.1. index.html: hàm seek(t)

Một vài khái niệm trong code:

  • canvas: thẻ HTML cho phép vẽ hình bằng JavaScript.

  • spring(): công thức lò xo để tạo chuyển động tự nhiên (giải thích ở mục 4.3).

  • rng(seed): hàm random có "hạt giống". Math.random mỗi lần cho số khác, nên hai lần render sẽ ra hai video khác nhau. rng(7) thì lúc nào cũng cho cùng một dãy số. mulberry32 (nhắc tới trong CLAUDE.md) là tên thuật toán này.

  • SCENES: danh sách các cảnh. Mỗi cảnh là một object { from, to, draw(t) }, thêm cảnh thì thêm object.

🤖 Claude tự viết. Đây là file index.html mẫu. Khi bạn dán prompt, Claude tự viết một phiên bản tương tự, bạn không cần gõ lại.

<style>html,body{margin:0;background:#141413}canvas{display:block}</style>
<canvas id="c" width="1080" height="1920"></canvas>
<script>
const W = 1080, H = 1920, DUR = 15;
const g = document.getElementById('c').getContext('2d');
const clamp = (x, a = 0, b = 1) => Math.min(b, Math.max(a, x));

// Closed-form damped spring, 0 → 1. Pure function of time.
function spring(t, k = 170, d = 26) {
  if (t <= 0) return 0;
  const w0 = Math.sqrt(k), z = d / (2 * w0);
  if (z < 1) {
    const wd = w0 * Math.sqrt(1 - z * z);
    return 1 - Math.exp(-z * w0 * t) * (Math.cos(wd * t) + (z * w0 / wd) * Math.sin(wd * t));
  }
  return 1 - Math.exp(-w0 * t) * (1 + w0 * t);      // z >= 1 treated as critical
}

// Seeded noise, never Math.random: the render must be identical every run
function rng(seed) { return () => { seed |= 0; seed = seed + 0x6D2B79F5 | 0;
  let t = Math.imul(seed ^ seed >>> 15, 1 | seed);
  t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t; return ((t ^ t >>> 14) >>> 0) / 4294967296; }; }

const SCENES = [
  { from: 0, to: 3, draw(t) {                        // kinetic title
      const s = spring(t - 0.1, 220, 22);
      g.save(); g.translate(W / 2, H / 2); g.scale(0.6 + 0.4 * s, 0.6 + 0.4 * s);
      g.globalAlpha = clamp(t * 4);
      g.fillStyle = '#F0EEE6'; g.font = '700 190px "Source Serif 4", serif';
      g.textAlign = 'center'; g.fillText('MOTION', 0, 0);
      g.fillStyle = '#D97757'; g.fillRect(-320 * s, 50, 640 * s, 18);
      g.restore();
  }},
  { from: 3, to: 6, draw(t) {                        // grid of squares on a stagger
      const r = rng(7);
      for (let i = 0; i < 48; i++) {
        const x = (i % 6) * 170 + 115, y = Math.floor(i / 6) * 170 + 360;
        const s = spring(t - i * 0.03 - r() * 0.1, 260, 20);
        g.fillStyle = i % 7 ? '#F0EEE6' : '#D97757';
        g.fillRect(x - 60 * s, y - 60 * s, 120 * s, 120 * s);
      }
  }},
  // ... more scenes: one object per shot
];

function draw(t) {
  g.fillStyle = '#141413'; g.fillRect(0, 0, W, H);
  for (const s of SCENES) if (t >= s.from && t < s.to) s.draw(t - s.from);
}
window.seek = (t) => { draw(t); return true; };

// Live preview in a normal browser, off during headless render
if (!navigator.webdriver) {
  const t0 = performance.now();
  (function loop() { draw(((performance.now() - t0) / 1000) % DUR); requestAnimationFrame(loop); })();
}
</script>

Cách hoạt động:

  • draw(t) xóa nền, tìm cảnh đang chiếu tại thời điểm t rồi gọi hàm vẽ của cảnh đó.

  • window.seek là "cổng" để Playwright gọi từ bên ngoài.

  • Đoạn cuối dùng để xem thử: mở file bằng trình duyệt thường thì animation tự chạy. Khi Playwright điều khiển, navigator.webdriver bằng true nên phần này tắt đi, để Playwright tự gọi seek.

4.2. render.mjs: xuất video

Lưu cạnh index.html, chạy node render.mjs --sub 1 để render nhanh. Kết quả ra out/silent.mp4.

🤖 Claude tự viết. Script render là việc của Claude khi làm video. Chỉ lưu file này nếu bạn muốn có sẵn khung để Claude dùng lại.

// node render.mjs --fps 60 --dur 15 --sub 4
import { chromium } from 'playwright';
import { spawn } from 'node:child_process';
import { mkdirSync } from 'node:fs';

const arg = (k, d) => { const i = process.argv.indexOf('--' + k); return i > 0 ? Number(process.argv[i + 1]) : d; };
const FPS = arg('fps', 60), DUR = arg('dur', 15), SUB = arg('sub', 4);
mkdirSync('out', { recursive: true });

const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1080, height: 1920 }, deviceScaleFactor: 1 });
await page.goto('file://' + process.cwd() + '/index.html');
await page.evaluate(() => document.fonts.ready);       // canvas text needs loaded fonts

// tmix averages SUB consecutive subframes; select keeps the last of each group
const vf = `tmix=frames=${SUB},select='eq(mod(n\\,${SUB})\\,${SUB - 1})',setpts=N/${FPS}/TB`;
const ff = spawn('ffmpeg', ['-y', '-f', 'image2pipe', '-framerate', String(FPS * SUB), '-i', '-',
  '-vf', vf, '-r', String(FPS), '-c:v', 'libx264', '-crf', '16', '-pix_fmt', 'yuv420p', 'out/silent.mp4'],
  { stdio: ['pipe', 'inherit', 'inherit'] });

const total = Math.round(DUR * FPS * SUB);
for (let i = 0; i < total; i++) {
  await page.evaluate((t) => window.seek(t), i / (FPS * SUB));
  const png = await page.locator('#c').screenshot({ type: 'png' });
  if (!ff.stdin.write(png)) await new Promise((r) => ff.stdin.once('drain', r));
  if (i % (FPS * SUB) === 0) console.log(`rendered ${i / (FPS * SUB)}s / ${DUR}s`);
}
ff.stdin.end();
await new Promise((r) => ff.on('close', r));
await browser.close();

Các điểm cần hiểu:

  • Tham số: --fps là số khung hình mỗi giây, --dur là độ dài (giây), --sub là số khung phụ (xem ngay dưới).

  • Motion blur với --sub 4: motion blur là vệt nhòe tự nhiên khi vật chuyển động nhanh, giống trong ảnh chụp. Để giả lập, mỗi khung hình được chụp thành 4 ảnh ở các thời điểm lệch nhau một chút (gọi là subframe), rồi ffmpeg chồng 4 ảnh lại lấy trung bình. Đổi lại, render lâu gấp 4 lần (15 giây video = 3.600 ảnh). Khi xem thử, hãy dùng --sub 1.

  • Định dạng xuất: H.264/yuv420p là định dạng MP4 mà điện thoại và mạng xã hội nào cũng phát được. CRF 16 là mức nén nhẹ, cho chất lượng cao.

4.3. Spring: chuyển động trông tự nhiên

Easing và spring khác nhau thế nào. Chuyển động "rẻ tiền" là easing: vật đi từ A tới B trong đúng X giây theo một đường cong định sẵn (ví dụ ease-in-out trong CSS). Chuyển động "đắt tiền" thì có khối lượng: tăng tốc, vượt quá đích một chút (overshoot) rồi mới dừng hẳn. Đó là spring, giả lập một chiếc lò xo. Các video giao diện biến hình ở mục 7.3 đều yêu cầu spring.

Tại sao phải dùng công thức (closed-form). Có hai cách tính lò xo:

  • Mô phỏng: tính từng bước, khung sau dựa vào khung trước. Muốn biết khung 812 thì phải chạy lại từ khung 0.

  • Công thức: đưa t vào là ra vị trí ngay, giống tra bảng.

Renderer nhảy thẳng tới từng t, nên bắt buộc dùng cách thứ hai. Hàm spring() ở mục 4.1 chính là công thức này.

Mẹo mà phần lớn mọi người bỏ lỡ: khi một giá trị đổi đích nhiều lần (ví dụ con trỏ chuột di chuyển qua nhiều điểm, hay chiều rộng của một khung), đừng khởi động lại spring cho mỗi đích mới, vì vật sẽ khựng lại rồi mới chạy tiếp. Thay vào đó, cộng dồn một spring cho mỗi lần đổi đích, mỗi spring bắt đầu ở thời điểm của riêng nó. Vật đang bay dở vẫn đổi hướng mượt sang đích mới, mà bạn vẫn render thẳng được khung 812 không cần chạy lại từ khung 0 tới 811.

Lưu đoạn code sau vào lib/motion.js, chép kèm hai hàm spring() và clamp() từ mục 4.1. Nếu muốn import file này vào index.html, đổi thẻ <script> thành <script type="module">.

🤖 Claude tự viết. Các hàm spring này Claude viết khi cần. Lưu sẵn thành lib/motion.js nếu muốn dùng prompt bên dưới hoặc skill ở mục 8.2.

// keys: [[time, value], ...] sorted by time. Returns the value at t.
export function track(t, keys, k = 170, d = 26) {
  let v = keys[0][1];
  for (let i = 1; i < keys.length; i++)
    v += (keys[i][1] - keys[i - 1][1]) * spring(t - keys[i][0], k, d);
  return v;
}

// Tab indicator that stretches: leading edge is stiffer than trailing edge
export function indicator(t, stops) {             // stops: [[time, x], ...]
  const lead  = track(t, stops, 320, 30);
  const trail = track(t, stops, 140, 22);
  return { left: Math.min(lead, trail), right: Math.max(lead, trail) + 120 };
}

// Text inside a morphing box: in after the morph starts, out before the next one
export function swapAlpha(t, tIn, tOut) {
  return Math.min(clamp((t - tIn - 0.08) / 0.12), clamp((tOut - 0.1 - t) / 0.1));
}

// Seamless loop: pin the last frame to the first
export const loopT = (t, dur) => ((t % dur) + dur) % dur;

Hàm

Tác dụng

track

Tính giá trị đổi đích nhiều lần, chuyển động liền mạch

indicator

Gạch chân tab co giãn như con sâu đo: mép trước chạy nhanh, mép sau đuổi theo

swapAlpha

Chữ trong khung đang biến hình hiện ra sau khi biến hình bắt đầu, ẩn trước lần kế tiếp

loopT

Quay vòng thời gian để video lặp liền mạch

Chỉnh độ nảy bằng k và d. k là độ cứng của lò xo: càng lớn thì chuyển động càng nhanh, càng gắt. d là độ giảm chấn: càng nhỏ thì càng nảy nhiều. Code mặc định dùng k = 170, d = 26. Tác giả chia spring thành 4 nhóm theo loại vật thể, kèm thông số cụ thể:

Nhóm

Dùng cho

k, d

Snappy

Nút bấm, công tắc, mép trước của thanh gạch tab

320, 30

Default

Thẻ, khung chứa, camera

170, 26

Heavy

Chữ lớn, vật thể 3D, logo kèm tên thương hiệu

90, 20

Playful

Mascot, sticker (cố ý cho thấy rõ độ nảy)

220, 14

Đường cong của 4 nhóm spring khi giá trị đi từ 0 tới 1: Playful vượt đích rõ nhất, Snappy tới đích nhanh nhất, Heavy chậm và không nảy

Nếu video đã có mà chuyển động còn cứng, dán prompt này, Opus sẽ sửa lại toàn bộ file trong một lượt:

Replace every easing curve with closed-form springs from lib/motion.js. Tiny overshoot on UI,
none on type. Any value with more than one target uses track().

5. Âm thanh

5.1. Vài khái niệm

  • Beat: nhịp phách của bài nhạc, chỗ bạn hay gõ chân theo.

  • BPM: số beat mỗi phút. 120 BPM nghĩa là cứ 0,5 giây có một beat.

  • Downbeat: phách mạnh ở đầu mỗi ô nhịp, thường cứ 4 beat có một.

  • Beat grid: lưới thời gian các beat, dùng để canh lúc nào hình thay đổi.

  • SFX: hiệu ứng âm thanh như tiếng click, tiếng vút (whoosh).

Âm thanh là chỗ "video AI" bắt đầu có cảm giác như phim thật. Rob Hallam từng nghĩ tiếng trong video của mình được ghép thêm sau. Không phải, Claude tự làm hết.

Để hình khớp nhạc có hai cách. Nếu có file nhạc, đo xem beat rơi vào giây nào (mục 5.2). Nếu không có, bảo Claude tự tạo âm thanh bằng code trên cùng dòng thời gian với hình, tức là tạo sóng âm từ công thức toán học, nên biết chính xác từng âm nằm ở đâu (mục 5.3).

Dù đi cách nào, hình, nhạc và hiệu ứng âm thanh đều chạy trên cùng một dòng thời gian. Phách mạnh (downbeat) dùng để chuyển cảnh, beat thường để đặt SFX, còn các đỉnh âm mà beats.py đo được cho biết chính xác chỗ đặt tiếng "hit":

Một dòng thời gian, ba làn: nhạc, hiệu ứng âm thanh và hình, 8 giây ở 120 BPM. Phách mạnh mang chuyển cảnh, beat mang SFX

Hai ví dụ cho cách thứ hai:

  • Phim về cuộc đời Steve Jobs của onur ozcan (@oozn): dài 2 phút, dựng bằng Remotion + React + SVG (khoảng 8.700 dòng code), 23 kiểu chuyển cảnh tự viết, nhạc nền tạo bằng Node, mọi lần cắt cảnh khớp đúng 120 BPM. Bằng chứng là nhạc tạo bằng code đủ sức "gánh" một phim 2 phút.

  • "Small print" của Vox: cốt truyện, hình vẽ và nhạc đều do Opus làm, không dùng ảnh có sẵn nào, render bằng HyperFrames. Opus tạo nhạc bằng Python, rồi xem lại bản render từng giây một để trau chuốt thêm một lượt.

5.2. Đo beat từ file nhạc

Cần numpy và librosa (mục 2.2). Đặt nhạc vào audio/track.wav, lưu script dưới thành beats.py, rồi chạy:

python beats.py audio/track.wav > beats.json

🤖 Claude tự viết. Mặc định Opus tự dùng Python để đo beat. Lưu sẵn thành beats.py nếu muốn tự chạy hoặc dùng cho skill ở mục 8.2.

# python beats.py song.wav > beats.json
import sys, json, numpy as np, librosa

y, sr = librosa.load(sys.argv[1], sr=None, mono=True)
tempo, frames = librosa.beat.beat_track(y=y, sr=sr, units="frames")
beats = librosa.frames_to_time(frames, sr=sr).round(3).tolist()

onset = librosa.onset.onset_strength(y=y, sr=sr)
peaks = librosa.util.peak_pick(onset, pre_max=3, post_max=3, pre_avg=3,
                               post_avg=5, delta=0.5, wait=10)
json.dump({
    "bpm": float(np.atleast_1d(tempo)[0]),
    "beats": beats,                                   # state changes go here
    "downbeats": beats[::4],                          # big moments go here
    "hits": librosa.frames_to_time(peaks, sr=sr).round(3).tolist(),  # SFX go here
}, sys.stdout, indent=1)

File beats.json có 3 danh sách mốc thời gian:

Trường

Là gì

Dùng để

beats

Các beat

Đổi trạng thái trên hình

downbeats

Phách mạnh, cứ 4 beat có một

Đặt khoảnh khắc lớn

hits

Chỗ âm thanh bật mạnh lên (tiếng trống...)

Đặt hiệu ứng âm thanh

5.3. Tạo hiệu ứng âm thanh bằng code

Mỗi âm là một sóng âm tắt dần theo thời gian:

  • click: âm cao, tắt rất nhanh

  • pop: âm cao dần, tắt nhanh

  • thump: âm trầm, tắt chậm

  • whoosh: tiếng xè xè to dần rồi nhỏ dần

Script đọc file cues.json, là danh sách "giây nào phát tiếng gì", ví dụ [{"t": 0.5, "type": "click"}]. Claude sẽ tạo file này từ beats.json hoặc từ các mốc chuyển cảnh. Kết quả ghi ra file WAV, không cần thư viện ngoài.

🤖 Claude tự viết. Hiệu ứng âm thanh được tạo bằng code như CLAUDE.md yêu cầu. Lưu sẵn thành sfx.mjs nếu muốn dùng cho skill ở mục 8.2.

// node sfx.mjs cues.json out/sfx.wav     cues: [{"t":0.5,"type":"click"}, ...]
import { readFileSync, writeFileSync } from 'node:fs';
const SR = 48000, cues = JSON.parse(readFileSync(process.argv[2], 'utf8'));
const buf = new Float32Array(Math.ceil((Math.max(...cues.map(c => c.t)) + 2) * SR));

let s = 42; const noise = () => (s = (s * 1664525 + 1013904223) >>> 0) / 2147483648 - 1;
const VOICES = {
  click:  [0.05, t => Math.sin(2 * Math.PI * 1800 * t) * Math.exp(-t * 90) * 0.5],
  pop:    [0.15, t => Math.sin(2 * Math.PI * (600 + 900 * t) * t) * Math.exp(-t * 30) * 0.4],
  thump:  [0.50, t => Math.sin(2 * Math.PI * (90 - 60 * t) * t) * Math.exp(-t * 9) * 0.9],
  whoosh: [0.35, t => noise() * Math.sin(Math.PI * Math.min(1, t / 0.35)) * 0.25],
};
for (const c of cues) {
  const [len, fn] = VOICES[c.type], start = Math.floor(c.t * SR);
  for (let i = 0; i < len * SR && start + i < buf.length; i++) buf[start + i] += fn(i / SR);
}

const n = buf.length, b = Buffer.alloc(44 + n * 2);      // 16-bit mono WAV
b.write('RIFF', 0); b.writeUInt32LE(36 + n * 2, 4); b.write('WAVEfmt ', 8);
b.writeUInt32LE(16, 16); b.writeUInt16LE(1, 20); b.writeUInt16LE(1, 22);
b.writeUInt32LE(SR, 24); b.writeUInt32LE(SR * 2, 28); b.writeUInt16LE(2, 32); b.writeUInt16LE(16, 34);
b.write('data', 36); b.writeUInt32LE(n * 2, 40);
for (let i = 0; i < n; i++) b.writeInt16LE(Math.round(Math.max(-1, Math.min(1, buf[i])) * 32767), 44 + i * 2);
writeFileSync(process.argv[3], b);

Nhạc nền thì giao cho Claude viết theo cách tương tự, chỉ phức tạp hơn.

5.4. Ghép tiếng vào video

CLAUDE.md yêu cầu âm lượng -14 LUFS. LUFS là đơn vị đo độ to mà tai người cảm nhận. -14 LUFS là mức YouTube và Instagram dùng, để video không bị nền tảng tự vặn nhỏ đi.

Bài gốc không đưa lệnh cụ thể cho bước này. Đây là một lệnh ffmpeg thông dụng để ghép và chuẩn hóa âm lượng:

🤖 Claude tự chạy. Nếu CLAUDE.md đã yêu cầu -14 LUFS, Claude thường tự ghép tiếng. Lệnh này để bạn tự làm khi cần.

ffmpeg -i out/silent.mp4 -i out/sfx.wav \
  -af loudnorm=I=-14:TP=-1 -c:v copy -c:a aac -shortest out/final.mp4

Trong thực tế, Claude thường tự làm bước này nếu CLAUDE.md đã có yêu cầu.


6. Cho Claude tự xem lại video

Opus 5.5 đọc được ảnh nhưng không xem được video. Vì vậy ta dùng ffmpeg cắt video thành ảnh để Claude "xem" chính thứ nó vừa render, chấm điểm và sửa. Theo tác giả, chỉ riêng thói quen này đã tách những clip viral khỏi những clip được đăng kèm câu "trông hơi mid".

Các video đẹp đều qua nhiều vòng như vậy, không phải "một prompt". Phim màu nước 45 giây của Mable Joseph nói thẳng: 163 lần gọi model, khoảng 6 tiếng 45 phút từ đầu tới cuối (trong đó chừng 1 tiếng rưỡi ngồi làm cùng). Video ngày launch của DreW (1,7 triệu view lúc bài gốc viết) cũng thấy rõ dấu vết của nhiều vòng dọn dẹp. Lặp đi lặp lại chính là phương pháp, không phải thất bại.

Vòng tự review: render ảnh tĩnh mỗi beat một khung, ghép contact sheet, chấm điểm 7 tiêu chí, sửa 3 lỗi tệ nhất và ghi vào review_log.md, lặp tới khi mọi điểm đạt 8+

6.1. Tạo ảnh để review

Thường thì Claude tự chạy các lệnh này, bạn chỉ cần biết để hiểu nó đang làm gì. Bài gốc chạy các lệnh này trên out/final.mp4. Ở đây mình đổi thành bản render nhanh out/silent.mp4 (render với --sub 1) để review cho nhanh trước khi render bản cuối; muốn kiểm tra bản cuối thì chỉ cần đổi tên file.

Contact sheet là một ảnh ghép nhiều khung hình nhỏ thành lưới, giống tờ ảnh thử của thợ ảnh phim, nhìn một lần là thấy cả video.

🤖 Claude tự chạy. Các lệnh này nằm trong vòng tự review. Bạn chỉ cần đọc để hiểu nó đang làm gì.

# Contact sheet: 2 khung hình mỗi giây, lưới 6 cột → nhìn tổng thể nhịp và bố cục
ffmpeg -i out/silent.mp4 -vf "fps=2,scale=270:-1,tile=6x5" -frames:v 1 out/contact.png

# 12 khung hình liên tiếp quanh giây 4.2 → bắt lỗi giật, chữ chồng lên nhau
ffmpeg -ss 4.1 -i out/silent.mp4 -vf "scale=320:-1,tile=12x1" -frames:v 1 out/strip.png

# Thu nhỏ còn 360px (cỡ màn hình điện thoại) → kiểm tra chữ có đọc được không
ffmpeg -i out/silent.mp4 -vf "fps=1,scale=360:-1,tile=5x3" -frames:v 1 out/phone.png

# Phát 2 lần liền nhau → kiểm tra chỗ nối khi video lặp lại có bị giật không
ffmpeg -stream_loop 1 -i out/silent.mp4 -c copy out/loop_check.mp4

Kiểm tra tính tất định: render hai lần rồi so mã hash (mã "vân tay" của file). Hai mã giống nhau nghĩa là hai file giống hệt nhau.

node render.mjs --dur 5 --fps 60 --sub 1 && md5 out/silent.mp4   # Linux: md5sum
# chạy lại lần nữa, so sánh hai mã

6.2. Prompt review

Lưu thành prompts/critique-pass.txt để dùng lại (skill ở mục 8.2 sẽ gọi file này). Dán vào Claude Code sau mỗi lần render.

Prompt yêu cầu Claude đóng vai đạo diễn khó tính, chấm điểm 1–10 theo 7 tiêu chí:

Tiêu chí

Ý nghĩa

Hook

2 giây đầu có đủ hút để người xem không lướt qua

Readability

Chữ đọc được trên màn hình điện thoại

Motion quality

Chuyển động mượt, không có khung hình "chết"

Variety

Cứ 2–4 giây có điều mới

Composition

Bố cục

Brand accuracy

Đúng màu, font, logo của thương hiệu

Sound sync

Âm thanh khớp hình

Sau đó Claude liệt kê 3 lỗi lớn nhất kèm thời điểm, sửa, render lại và chấm lại. Lặp tới khi mọi tiêu chí đạt từ 8 điểm.

Open out/contact.png, out/strip.png and out/phone.png and look at them properly.
Be a harsh motion director, not a proud author.

Score 1-10: hook in first 2s · readability at phone size · motion quality (springs,
no dead frames) · variety (new thing every 2-4s) · composition · brand accuracy · sound sync.

List the 3 biggest problems with timestamps. Hunt specifically for: text overlapping during
swaps, anything sliding instead of easing, corner labels and frame borders, centered-on-gradient
shots, blurry scaled text, a dead beat with nothing happening, a stutter at the loop seam.

Fix them, re-render only the affected seconds, show me the new contact sheet and new scores.

7. Viết prompt cho video thật

Các prompt dưới đây dán vào ô chat của Claude Code. Thay các chỗ [...] bằng thông tin của bạn.

7.1. Video giới thiệu sản phẩm

Đây là phần hữu ích nhất nếu bạn đang bán một sản phẩm. Tony Dinh làm video giới thiệu TypingMind trong chưa tới 30 phút, trong khi một năm trước Tony trả khoảng $1.000 để thuê làm video tương tự. Bí quyết chỉ là ba dòng thêm vào prompt showreel (prompt đầy đủ nằm ở reply thứ hai của bài):

  • URL của sản phẩm

  • "use actual product screenshot, logo, assets" (dùng ảnh chụp, logo, tài nguyên thật của sản phẩm)

  • "must have music" (bắt buộc có nhạc)

Opus tự vào website và tự gom tài nguyên.

Template dưới mở rộng ý tưởng đó, yêu cầu Claude:

  1. Vào website, chụp màn hình, lấy logo, màu và font thật. Không tự vẽ lại giao diện.

  2. Kể chuyện theo 5 nhịp: vấn đề → sản phẩm xuất hiện → 3 tính năng → một con số chứng minh → logo và lời kêu gọi hành động (CTA, ví dụ "Dùng thử miễn phí").

  3. Nhạc 120 BPM tự tạo, tiếng click và whoosh khớp nhịp.

  4. Xuất khung dọc 9:16 trước, sau đó vuông 1:1 và ngang 16:9.

  5. Gửi contact sheet cho bạn duyệt trước khi render bản cuối.

Make a dynamic 20-second motion graphics video for [PRODUCT] ([URL]), with the energy
of a motion designer's showreel. Go all out.

Assets
- Visit the site. Use real screenshots (Playwright), the real logo, real colors and fonts.
  Save everything to ./assets and list what you found before you animate.
- Never redraw the product UI from imagination. Crop and animate the real thing.

Story (one beat each, 2 to 4 seconds)
1. Hook: the problem in 5 words of huge kinetic type.
2. The product appears, the UI assembles itself piece by piece.
3. Three features, each as a UI moment with a cursor doing a real action.
4. One number that proves it works: [METRIC].
5. Logo lockup + [CTA].

Sound
- Original music, 120 BPM, synthesized in code. UI clicks and whooshes on the beat.

Format: 1080x1920 (9:16) first, then 1:1 and 16:9 from the same timeline.
Before the full render, show me a contact sheet of one frame per beat.

(kinetic type: chữ chuyển động theo nhịp; logo lockup: logo đặt cạnh tên thương hiệu theo bố cục cố định)

Lưu ý:

  • Mỗi thương hiệu làm trong một phiên Claude Code (không /clear, không mở phiên mới). Sau showreel đầu tiên, Rob Hallam xin thêm một video quảng cáo sản phẩm ngay trong cùng cuộc trò chuyện, và nó ra nhanh hơn hẳn vì Claude đã có sẵn renderer, bộ tạo âm thanh và quy trình xuất file.

  • Thêm nhân vật biết nói. Chain (@achxvi) đẩy ý tưởng này xa hơn: video cho Pocketsflow (làm trong 15 phút) được đưa kèm một key ElevenLabs (dịch vụ tạo giọng đọc bằng AI) và yêu cầu một nhân vật đứng ra giải thích sản phẩm. Giọng đọc + mascot giờ là dịch vụ Chain đang bán (xem mục 8.5).

  • API key để trong file .env, trong prompt chỉ ghi tên biến, ví dụ "the ElevenLabs key is ELEVENLABS_API_KEY in .env". Đừng bao giờ dán key thật vào prompt mà bạn định chụp màn hình chia sẻ. Prompt công khai của Chain cũng dùng một key giả cho vui, đúng vì lý do này.

7.2. Dùng video hoặc ảnh mẫu

Không có mẫu tham khảo, Claude sẽ quay về style mặc định: chữ căn giữa, nền gradient, mọi thứ hiện dần.

Rexan Wong thấy video "một prompt" của mình trông "mid", nên đã xem lại hàng chục clip viral để tìm cách người ta thật sự làm. Kết luận: gọi đúng tên một phong cách hiệu quả hơn hẳn việc mô tả nó (ví dụ "Swiss typography", "phong cách keynote của Apple"), và một video hay ảnh mẫu cho Claude biết nhịp cắt cảnh, font và kiểu chuyển cảnh để bắt chước. Đó cũng là lý do rất nhiều video "một prompt" trông giống nhau.

Hai bài của Pleometric cho thấy các nấc dùng mẫu: video "feed TikTok của Opus" viết hoàn toàn bằng code, bắt đầu chỉ từ một khung hình của một animation viral khác. Video thứ hai làm theo quy trình của Donald (mục 8.3), lấy thư viện ảnh phong cách PC-98 của chính Pleometric làm mẫu. Thư viện ảnh của riêng bạn là mẫu tham khảo không ai sao chép được.

Loại mẫu

Cách dùng

Một ảnh

Chụp một khung hình từ video bạn thích, đính kèm, nói rõ lấy gì (màu, font, độ nhiễu hạt) và không lấy gì (chủ thể)

Một video

Đưa Claude file hoặc link, bảo nó cắt thành ảnh bằng ffmpeg và mô tả nhịp từng cảnh trước khi code

Thư viện ảnh

Folder ảnh hoặc sản phẩm cũ của bạn, yêu cầu Claude viết style_guide.md từ đó trước

Tìm video mẫu ở: whatships.com (video launch sản phẩm), Dribbble mục motion, video launch của đối thủ.

(PC-98: dòng máy tính Nhật thập niên 80–90, nổi tiếng với đồ họa pixel ít màu)

Đặt video mẫu vào ./refs/launch.mp4 rồi dán prompt dưới. Claude sẽ tự cắt ảnh, viết bản mô tả phong cách (style_guide.md) và danh sách cảnh (shotlist.md), rồi dừng lại chờ bạn duyệt. Nếu không ưng, hãy bảo Claude viết lại danh sách cảnh, đừng sửa code. Câu quan trọng nhất là "Take the grammar of the reference, never its content": học cách làm (nhịp, bố cục, chuyển cảnh), không chép nội dung (logo, nhân vật).

Quy trình dùng mẫu: video mẫu, cắt khung hình bằng ffmpeg, viết style_guide.md, viết shotlist.md theo từng beat, chờ bạn duyệt rồi mới code
Reference: ./refs/launch.mp4 (and ./refs/frames/*.png)

1. Extract one frame every 0.5s with ffmpeg. Study them.
2. Write ./docs/style_guide.md: palette (hex), type (family, weight, tracking),
   shot lengths, transition types, camera moves, texture/grain, how text enters and exits.
3. Write ./docs/shotlist.md for a [DURATION]s video about [SUBJECT] in THAT style.
   Take the grammar of the reference, never its content, logos or characters.
4. Show me both files. Wait for my OK before any code.

Khi đã đưa mẫu, hãy để Claude tự chọn kỹ thuật: chỉ nói video phải trông thế nào và giới hạn gì (màu, font, độ dài), đừng chỉ định thư viện, trừ khi bạn cần một framework cụ thể để dùng lại về sau. Pleometric từng gợi ý dùng p5.js (thư viện vẽ đồ họa), nhưng Opus chọn tự viết một renderer riêng mô phỏng chất giấy.

7.3. Video giao diện biến hình (spec dạng danh sách trạng thái)

Nếu bạn mới làm video đầu tiên, có thể bỏ qua mục này và quay lại sau.

Prompt được bookmark nhiều nhất tuần đó không phải prompt một dòng. Video giao diện biến hình của zero (@twoclipping) (907 nghìn view, 19 nghìn bookmark lúc bài gốc viết) và phim giới thiệu MakerMap của Martijn Verbove đều dùng một bản spec dạng XML, gồm: thông tin cần hỏi, định hướng phong cách, danh sách trạng thái theo từng nhịp, luật kỹ thuật và các lỗi cần tránh. Martijn chỉ đưa Opus xem một bài motion viral và nói "explain MakerMap, but make it insane" (giải thích MakerMap, nhưng làm cho thật điên rồ).

Ý tưởng chung:

  • Cả video chỉ có một hình khối, không bao giờ cắt cảnh.

  • Hình khối này liên tục biến hình (morph): đổi kích thước, độ bo góc, màu, lần lượt thành nút bấm → thanh loading → trình phát nhạc → thanh trượt → biểu đồ → ô tìm lệnh ⌘K...

  • Mỗi lần biến hình đều do một con trỏ chuột bấm, kéo thật sự.

  • Khung hình cuối trùng khung hình đầu nên video lặp liền mạch.

Một hình khối duy nhất đổi kích thước, độ bo góc và màu: nút bấm, vòng loading, dấu tích, trình phát nhạc, biểu đồ, ô tìm lệnh, rồi quay về nút bấm để lặp liền mạch

Dev sẽ thấy quen: đây là một state machine (chuỗi trạng thái), chỉ khác là mỗi lần đổi trạng thái được quay lại thành video. Phim của SuSu (@NFT_Chen) là cùng ý tưởng đó dưới dạng câu chuyện: Opus tự code một phần mềm dựng phim hoạt hình rồi "chui vào" dựng phim trong đó, từ timeline, sóng âm tới nút xuất file đều là code vẽ ra. Một giao diện giả giống như một phim trường, nơi mọi thành phần đều có trạng thái biết trước.

Template dưới đây là bản của tác giả bài gốc, dựng theo cấu trúc trên. Bản gốc của zero và Martijn nằm trong thread của họ (link ở cuối bài), nội dung cụ thể có khác. Template có 6 phần:

Thẻ

Ý nghĩa

<inputs>

Claude sẽ hỏi bạn những gì (sản phẩm, 8–12 trạng thái, màu, nhạc)

<direction>

Phong cách và những điều cấm

<structure>

Trình tự các trạng thái theo nhịp nhạc

<build>

Yêu cầu kỹ thuật (đã giải thích ở mục 4–5)

<gotchas>

2 lỗi hay gặp: chữ bị mờ khi phóng to, và chỗ nối khi lặp

<start>

Bắt Claude hỏi thông tin và trình bày danh sách trạng thái trước khi code

<inputs>
Ask me for: my product + URL, 8 to 12 UI states that tell its story, the real data shown in
each state, brand colors + fonts + one accent, a royalty-free track near 120 BPM, formats.
</inputs>

<direction>
Product-film UI motion. One container never cuts: every state is the same element changing
size, radius and fill while its content swaps behind a short blur. A cursor drives every change.
Warm neutral canvas, one accent. Springs with at most a tiny overshoot.
Banned: bouncy easing, glows, gradients on UI chrome, particle bursts, dead time.
</direction>

<structure>
120 BPM, 8 bars, something happens on every beat.
logo → CTA button → email field (typed) → loader → success check → dashboard card
→ chart draws itself → tooltip on hover → ⌘K palette → toast → logo.
</structure>

<build>
1. One HTML file, one canvas, window.seek(t). No CSS transitions, no timers, no carried state.
2. Closed-form springs. A value with many targets = sum of one spring per change.
3. Text inside a morphing container enters after the morph starts, leaves before the next one.
4. Tab indicators: leading and trailing edges on different springs so they stretch.
5. Beat grid from the track (numpy/librosa). Start on a downbeat. UI sounds on measured peaks.
6. Render in headless Chrome at 60 fps, 4 subframes per frame, blended for motion blur.
</build>

<gotchas>
Never use will-change on anything the camera scales (blurry text).
The last frame must equal the first, cursor position and velocity included.
</gotchas>

<start>
Ask for the inputs, then show me the state list on the beat grid before writing code.
</start>

Phần <structure> được trải lên lưới nhịp như hình dưới: 120 BPM tức 0,5 giây một beat, mỗi trạng thái đổi trên một phách mạnh (cứ 2 giây một lần). 8 ô nhịp là 16 giây, và giây thứ 16 quay về đúng logo ban đầu.

Lưới nhịp 120 BPM: logo, CTA, ô email, loading, dấu tích, thẻ dashboard, biểu đồ, ô tìm lệnh, mỗi trạng thái cách nhau 2 giây, giây 16 quay lại logo

8. Nâng cao

8.1. Xuất nhiều tỉ lệ khung hình

Ba tỉ lệ phổ biến: 9:16 (dọc, cho TikTok/Reels), 1:1 (vuông), 16:9 (ngang, cho YouTube).

Mọi tỉ lệ nên xuất ra từ cùng một dòng thời gian. Muốn vậy, hãy viết cảnh dựa trên một hàm bố cục (layout) thay vì số pixel cố định. Ví dụ, thay vì x = 540 thì viết x = W / 2, thay vì cỡ chữ 190px thì viết H * 0.1. Khi đổi khung sang 1080×1080, bố cục tự co lại. Sau đó bảo Claude render song song ba tỉ lệ (spec MakerMap ở mục 7.3 yêu cầu đúng việc này), và sắp xếp lại chữ, giao diện cho từng tỉ lệ, đừng cắt bản 16:9 thành bản dọc.

Một dòng thời gian, ba tỉ lệ: 9:16 cho Reels và TikTok, 1:1 cho feed X, 16:9 cho YouTube và website. Sắp xếp lại chữ và giao diện, không cắt xén

8.2. Đóng gói thành skill

Skill là một file hướng dẫn mà Claude Code tự nạp khi bạn gõ /tên-skill. Nhờ đó bạn không phải dán lại prompt dài mỗi lần.

Lưu nội dung dưới vào .claude/skills/motion-reel/SKILL.md. Từ đó, cả bài hướng dẫn này gói gọn trong một câu lệnh:

/motion-reel for [URL], 20s, vertical, reference ./refs/frame.png

Skill này dùng lại các file đã tạo ở các mục trước: lib/motion.js (mục 4.3), beats.py và sfx.mjs (mục 5), prompts/critique-pass.txt (mục 6.2).

---
name: motion-reel
description: Make a product or showreel motion video rendered from code. Use when the
  user asks for a launch video, showreel, product reel, animated explainer or motion ad.
---

# Motion reel

## Inputs to collect first
Product + URL, duration, formats (9:16 / 1:1 / 16:9), brand colors + fonts,
a reference (frame, video or image folder), music (file or "synthesize").

## Pipeline
1. Gather assets from the URL with Playwright into ./assets. List them.
2. If a reference exists, write docs/style_guide.md from it.
3. Measure or synthesize music. beats.py → beats.json.
4. Write docs/shotlist.md on the beat grid. Show it and wait for OK.
5. Build index.html with window.seek(t) using lib/motion.js springs. Follow CLAUDE.md.
6. Contact sheet → critique-pass (see prompts/critique-pass.txt) → fix. 3 rounds minimum.
7. node render.mjs → sfx.mjs → mix to -14 LUFS → out/final.mp4, all formats.
8. Deliver final.mp4, contact.png, poster.png. Say what you'd improve next.

## Hard rules
- Real product UI only. Never invent screens.
- No Math.random, no timers, no CSS transitions in render mode.
- Banned: corner labels, centered title on gradient, everything fading in.

Có thể chia sẻ thư mục skill này cho cả team, hoặc đóng gói thành plugin công khai như buildwithhanif đã làm.

8.3. Video dài: viết brief như đạo diễn

Donald nói với máy tính trong 5 phút, đi ngủ, Claude làm việc 12 tiếng, sáng dậy đã có một MV dài 142 giây, sau đó đạt 2,1 triệu view (lúc bài gốc viết). Prompt công khai của Donald dài khoảng 9.500 ký tự, đại ý: đây là file MP4 và link bài hát "Claude Pop", hãy làm lại MV này từ đầu tới cuối, giữ nguyên bản nhạc, dùng animation JavaScript có cảm giác như giấy, được tự do lên mạng tìm tham khảo và đẩy thẩm mỹ theo hướng nào cũng được. Phim robot "Pip" của @pradeepXkapoor còn dùng brief dài 19.000 ký tự.

Đọc bản nào cũng thấy cùng một bộ khung: chúng không mô tả một video, mà thuê cả một ekip. Một brief tốt gồm:

  1. Phim trong một câu (logline): nội dung và điểm "chốt" của phim, để mọi quyết định đều so lại được với nó.

  2. Tham khảo: video gốc, bài nhạc, thư viện ảnh, repo GitHub chứa sản phẩm cũ. Giữ gì, đẩy xa thêm ở đâu.

  3. Công cụ và key: skill cần nạp, API có sẵn (tạo ảnh, video, giọng nói), ngân sách, tài liệu nằm ở đâu. Kèm câu nhắc "dùng tiết kiệm".

  4. Hồ sơ nhân vật (character bible): tỉ lệ, bảng màu lấy từ một bản thiết kế nhân vật, các biểu cảm, và một "khóa nhận diện" để nhân vật vẫn nhận ra được dù phong cách vẽ thay đổi thế nào.

  5. Kịch bản theo thời gian (beat sheet): các hồi có mốc thời gian, cứ 3–5 giây có một điểm nhấn hình ảnh, 2 giây đầu phải giữ được người xem.

  6. Chữ trên màn hình: khi nào lời bài hát hay chú thích phóng to chiếm màn hình, khi nào nằm gọn như phụ đề. Bố cục phải chừa chỗ cho chữ.

  7. Quy trình có điểm duyệt: lên kế hoạch → dựng khung xương nhân vật (rig) → ảnh tĩnh từng cảnh (stills) → bản chuyển động nháp để canh nhịp (animatic) → làm đầy đủ → trau chuốt → âm thanh → render. Không được bỏ bước.

  8. Vòng tự review: render ảnh tĩnh, chấm điểm, ghi 3 lỗi tệ nhất, sửa, lặp tới khi mọi tiêu chí đạt 8+.

  9. Sản phẩm bàn giao: MP4 bản cuối, bản kiểm tra vòng lặp, ảnh bìa, contact sheet, mã nguồn sạch có README.

Kỹ thuật generate-then-trace (tạo trước, đồ lại sau). Đây là nước đi quan trọng nhất trong brief của Donald. Seedance 2.5 (một AI tạo video) render các cảnh nền có nhân vật và chuyển động vật lý. Sau đó Opus vẽ lại toàn bộ video bằng JavaScript đè lên trên, người xem chỉ thấy lớp do code vẽ, giống họa sĩ đồ lại theo ảnh mẫu. AI video lo phần chuyển động khó viết tay (người đi, vải bay), còn lớp JavaScript cho một phong cách thống nhất và thuộc về riêng bạn. Pleometric làm lại theo đúng quy trình đó với thư viện ảnh PC-98 của mình và ra một bộ phim hoàn toàn khác (vẫn để nguyên vài lỗi nhỏ để giữ đúng tinh thần làm một lượt).

Sơ đồ của tác giả cho thấy toàn bộ quy trình: từ 5 phút nói (khoảng 9.500 ký tự), Opus làm đạo diễn và điều phối ba nhánh: bản thiết kế nhân vật (tạo bằng model ảnh qua fal), các cảnh nền (Seedance 2.5, có khớp khẩu hình) và âm thanh (ElevenLabs cùng bài hát gốc). Lớp JavaScript vẽ đè lên trên, kiểm tra độ khớp, chạy lại khi cần, và ra MV 142 giây.

Quy trình generate-then-trace của Donald: Opus làm đạo diễn, điều phối thiết kế nhân vật, cảnh nền từ Seedance 2.5 và âm thanh, rồi lớp JavaScript vẽ lại toàn bộ thành MV 142 giây

Chia việc cho subagent. Subagent là các phiên Claude con chạy song song, mỗi phiên làm một đoạn phim. Repo PDoomVideo của John Heibel (1,1 nghìn sao) là ví dụ thực tế: Opus viết ANIMATION_GUIDE.md để giao việc cho các subagent chạy song song (để mọi subagent code cùng một phong cách), viết STORYBOARD.md sau lượt làm đầu tiên, và phim chia thành 9 chương trong src/ch/. Khi viết brief, hãy gọi đích danh hai file này.

Chia việc cho subagent: phiên chính làm đạo diễn, viết STORYBOARD.md và ANIMATION_GUIDE.md, 9 subagent làm 9 chương ch01.js tới ch09.js, rồi ghép và render qua timeline.js

Template (thay các chỗ [...]):

You are the director, animator, sound designer and render engineer for a [DURATION] film
made in code. Treat this as a multi-session production. Don't rush to a final render.

## The film in one line
[LOGLINE. What the viewer should feel at the end.]

## References and inputs
- ./refs/ : [video / frames / image library]. Take the grammar, never the content.
- ./audio/track.wav : use it unchanged. Measure beats with beats.py first.
- Skills available: [/remotion-best-practices | /hyperframes | /claude-animation].
- APIs in .env: [ELEVENLABS_API_KEY, FAL_KEY]. Budget: [$X]. Be economical.

## Look
[3-5 lines: palette, type, texture, camera language. Banned looks.]

## Beat sheet
0:00-0:02  hook: [the single most striking image]
0:02-0:10  [act 1]
...        a new visual payoff every 3-5 seconds
[END]      the last frame sets up the first frame (loop)

## Workflow, with gates
1. Write docs/style_guide.md and docs/shotlist.md (every shot: frames, camera, text, SFX).
   Show me the shot list. Then continue without waiting if I don't answer in 10 minutes.
2. Build stills for every shot. Contact sheet. Critique.
3. Animatic at 960x540 with placeholder audio. Fix pacing before polish.
4. Full animation, polish pass, sound pass, final render.
5. Split work across subagents per chapter. Write docs/ANIMATION_GUIDE.md first
   so every subagent codes in the same style.

## Critique loop (every shot, at least 3 rounds)
Render 3-5 stills, score 1-10 on: hook, readability at 360px wide, motion, composition,
depth, sound sync, polish. Log scores + 3 biggest problems in docs/review_log.md. Fix. Repeat
until all are 8+.

## Deliverables
out/final.mp4 · out/loop_check.mp4 · out/poster.png · out/contact.png · README.md

Lưu ý về dòng "continue without waiting if I don't answer in 10 minutes": đây là cách tác giả gốc dùng để Claude không dừng lại chờ duyệt. Tuy nhiên Claude không tự đếm thời gian được. Khi Claude đã dừng để hỏi thì nó sẽ chờ tới khi bạn trả lời. Nếu muốn để Claude chạy qua đêm, hãy viết thẳng là "không cần chờ duyệt, cứ làm tiếp".

8.4. Dùng framework Remotion hoặc HyperFrames

Đây là route B: cùng prompt, nhưng để framework lo phần render. Remotion và HyperFrames là hai framework chuyên làm video bằng code, có sẵn giao diện xem trước và timeline. Mỗi framework có bộ skill riêng cho Claude: Remotion thêm các lệnh như /remotion-create, /remotion-render; HyperFrames thêm lệnh /hyperframes (tự điều hướng tới skill phù hợp) và các skill GSAP.

Cách

Phù hợp khi

seek(t) tự viết

Video đơn lẻ, muốn kiểm soát hoàn toàn, không cài thêm gì

Remotion (React)

Làm series video, video theo mẫu, video sinh từ dữ liệu

HyperFrames (HTML)

Bạn quen tư duy theo trang web (HTML/CSS, thư viện animation GSAP)

Remotion. Chạy trong terminal:

npx create-video@latest launch-film && cd launch-film
npx skills add remotion-dev/skills
claude

Gõ trong Claude Code:

/remotion-create a 20s 9:16 launch film for [PRODUCT], springs only, one accent color

Xem trước và render:

npx remotion studio
npx remotion render Main out/launch.mp4

HyperFrames. Chạy trong terminal:

npx hyperframes init my-video && cd my-video
npx hyperframes skills update
claude

Gõ trong Claude Code:

Using /hyperframes, turn ./notes.md into a 45-second pitch video with kinetic captions

Xem trước và render:

npx hyperframes preview && npx hyperframes render

8.5. Biến thành dịch vụ kiếm tiền

Nếu muốn, bạn có thể làm như Chain (@achxvi) ngay trong tuần đó: bán nó như một dịch vụ. Gói dịch vụ của Chain là một mẫu tốt để tham khảo:

  • Có nhạc

  • Mascot theo bất kỳ phong cách nào

  • Giới thiệu tính năng sản phẩm

  • Lời mời chào (offer) ở cuối video

  • Làm bằng bất kỳ ngôn ngữ nào

  • Tối đa 3 lần chỉnh sửa

Còn giá thì lấy con số của Tony Dinh làm mốc: một năm trước, người ta trả khoảng $1.000 cho một video như vậy. Có skill và vòng tự review trong tay, bạn có thể giao hàng trong một buổi chiều. Video dọc 38 giây của Chain làm trong 45 phút, kèm reply chào bán dịch vụ, là ví dụ cụ thể.


Hạn chế cần biết

  • Chi phí: effort max và nhiều vòng review tốn khá nhiều token (xem con số của Mable Joseph ở phần Yêu cầu). Video dài nên ước tính ngân sách trước.

  • Thời gian render: 60fps với 4 khung phụ khá chậm. Nên xem thử ở --sub 1.

  • Không thay được cảnh quay thật: code làm tốt giao diện, chữ, hình khối, minh họa. Cảnh có người thật vẫn cần AI tạo video hoặc quay thật.

  • Không phù hợp khi cần chỉnh tay từng chi tiết như After Effects, hoặc team không ai đọc được code.

  • Video dễ bị giống nhau nếu chỉ copy prompt. Mẫu tham khảo và ý tưởng riêng mới tạo khác biệt.

Tóm tắt quy trình

  1. Cài Node, ffmpeg, Playwright. Tạo CLAUDE.md.

  2. Mở Claude Code, đặt effort xhigh, chạy prompt một dòng để kiểm tra setup.

  3. Viết prompt cho video thật, kèm URL sản phẩm hoặc video mẫu.

  4. Để Claude viết index.html, render nháp bằng --sub 1.

  5. Thêm âm thanh: đo beat từ nhạc có sẵn hoặc để Claude tự tạo.

  6. Cho Claude xem contact sheet, chấm điểm và sửa tới khi đạt 8+.

  7. Render bản cuối, xuất nhiều tỉ lệ, đóng gói thành skill để dùng lại.

Như tác giả kết bài: prompt một dòng cho bạn một clip, còn harness cho bạn cả một studio. Cài đủ công cụ, "mượn" một mẫu tham khảo, viết danh sách trạng thái, làm chủ engine seek(t), và bắt Opus xem lại khung hình của chính nó tới khi mọi tiêu chí đạt 8. Sau đó gói tất cả thành skill, và bạn sẽ không bao giờ phải viết lại prompt dài nữa.

Thuật ngữ

Thuật ngữ

Giải thích

Frame / fps

Một khung hình / số khung hình mỗi giây

Tất định (deterministic)

Cùng đầu vào luôn ra cùng kết quả

seek(t)

Hàm "tua tới giây t" và vẽ đúng khung hình ở giây đó

Headless browser

Trình duyệt chạy ngầm, không hiện cửa sổ, điều khiển bằng code

Canvas

Thẻ HTML cho phép vẽ hình bằng JavaScript

Seed

"Hạt giống" cho hàm random, cùng seed luôn ra cùng dãy số

Easing

Đường cong tốc độ khi vật đi từ A tới B

Spring

Chuyển động giả lập lò xo: lao tới, vượt nhẹ rồi nảy về

Overshoot

Đoạn vật vượt quá đích trước khi dừng

Morph

Biến hình mượt từ dạng này sang dạng khác

Subframe / motion blur

Khung phụ chụp lệch thời gian / vệt nhòe khi vật di chuyển nhanh

Showreel

Video ngắn khoe tay nghề, như portfolio dạng video

Hook

2 giây đầu giữ người xem không lướt qua

Beat / BPM / downbeat

Nhịp phách / số nhịp mỗi phút / phách mạnh đầu ô nhịp

SFX

Hiệu ứng âm thanh

LUFS

Đơn vị đo độ to cảm nhận, -14 là chuẩn mạng xã hội

Contact sheet

Một ảnh ghép nhiều khung hình nhỏ thành lưới

Effort

Mức "suy nghĩ kỹ" của Claude

Skill

File hướng dẫn cài vào Claude Code, gọi bằng /tên

Subagent

Phiên Claude con chạy song song

Brief

Bản mô tả yêu cầu chi tiết giao cho Claude

Harness

Bộ khung bao quanh Claude: engine render, file luật, âm thanh, vòng tự review

Route A / route B

Tự viết engine seek(t) / dùng framework như Remotion, HyperFrames

Repo tham khảo

Nguồn

Bài gốc: Movez (@0xMovez), How to build motion design studio with Opus 5.5 (Full-course), 27/09/2026. Code, prompt template và cấu trúc quy trình là của tác giả. Theo dõi thêm tại movez.substack.com. Các hình minh họa trong bài là của tác giả bài gốc; bảng 4 cấp độ, bảng 4 route và thông số spring được chuyển từ hình sang chữ. Những phần do người viết bổ sung: lệnh ghép âm thanh ở mục 5.4, lưu ý về dòng "10 minutes" ở mục 8.3, và các phần giải thích thuật ngữ. Số view được ghi theo thời điểm bài gốc đăng.

Các bài được trích dẫn:

Bài gốc còn nhắc tới @kloss_xyz, @1littlecoder, @sonnylazuardi (các biến thể prompt ở mục 3) và @pradeepXkapoor (phim "Pip", mục 8.3) nhưng không kèm link.

Bình luận

Đang tải bình luận…

Để lại bình luận

Nhận bài mới qua email

Có bài mới thì mình gửi một email. Không quảng cáo, không spam, hủy lúc nào cũng được.