E1 · Two formats of the example
10 minRun the "brand, check, two renders and conferir" block.
Done when: OK: 15,0 s · 1920×1080 · 30 fps · sem áudio and OK: 15,0 s · 1080×1920 · 30 fps · sem áudio, each with saída: 0.
Lesson 8 · 20 min
Time to put it all together into a piece you actually use: a 15 s opening with your DESIGN.md, in the two formats social networks ask for. The 9:16 doesn't come from the 16:9 through a render option: it's another composition that reuses the CSS and the choreography. In this lesson you deliver both, checked by the kit, and build the plan with dates.
Start the lesson
Goal: deliver a 15 s opening in 16:9 and 9:16, with the DESIGN.md, checked by the kit in both formats.
The four deliverables the opening brings together. Click a step to see the "done when".
init e render
3 exercises
HyperFrames project that renders and passes the checker.
See the lessonRISE e quadros
3 exercises
A composition the agent made and you checked across five frames.
See the lessonThe project from lesson 2, with the version pinned and telemetry turned off. This is the folder where the opening will live.
The DESIGN.md from lesson 4 sets the colors and the motion for both formats.
The RISE request from lesson 3, now for 15 s and two formats. The E includes the check and the frames of both.
If your opening has a voice or a soundtrack, lesson 7 adds the sound. If it doesn't, skip it: the checker says "sem áudio" and it's right.
Lessons 1, 5 and 6 are the base: determinism, Remotion and libraries come in when your piece calls for them.
abertura.css and abertura.js are shared; index.html (1920×1080) and formatos/vertical.html (1080×1920) only change the root and one class.
/* DESIGN.md tokens + a layout that works for both formats (16:9 and 9:16) */
:root { --fundo: #0c0c10; --superficie: #1c1c22; --linha: #3f3f47; --texto: #f4f4f5; --texto2: #a1a1aa; --destaque: #e9b949; }
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { overflow: hidden; background: var(--fundo); }
#root { width: 100%; height: 100%; position: relative; font-family: ui-sans-serif, system-ui, sans-serif; }
#luz { position: absolute; inset: 0; background: radial-gradient(ellipse at 50% 45%, var(--superficie) 0%, var(--fundo) 70%); }
#bloco { position: absolute; left: 10%; right: 10%; top: 50%; transform: translateY(-50%); }
#rotulo { color: var(--texto2); font-size: 40px; letter-spacing: 0.2em; text-transform: uppercase; line-height: 1.3; }
#titulo { color: var(--texto); font-size: 150px; font-weight: 700; line-height: 1.1; margin-top: 36px; }
#apoio { color: var(--texto2); font-size: 54px; line-height: 1.2; margin-top: 40px; }
#trilho { position: absolute; left: 10%; right: 10%; bottom: 14%; height: 4px; background: var(--linha); transform-origin: left center; }
#cabeca { position: absolute; left: 10%; bottom: calc(14% - 38px); width: 18px; height: 80px; background: var(--destaque); border-radius: 4px; }
/* 9:16: same piece, larger type relative to the width, everything stacked */
.vertical #titulo { font-size: 132px; }
.vertical #apoio { font-size: 56px; }
.vertical #rotulo { font-size: 36px; }// The same choreography in both formats: only the playhead's travel distance depends on the width.
window.criaAbertura = function () {
const root = document.getElementById("root");
const largura = Number(root.dataset.width);
const corrida = largura * 0.8 - 18; // from the start to the end of the track (80% of the width)
const tl = gsap.timeline({ paused: true });
tl.fromTo("#trilho", { scaleX: 0 }, { scaleX: 1, duration: 1.2, ease: "power2.inOut" }, 0.4);
tl.fromTo("#cabeca", { opacity: 0 }, { opacity: 1, duration: 0.3 }, 1.4);
tl.fromTo("#rotulo", { y: 24, opacity: 0 }, { y: 0, opacity: 1, duration: 0.6, ease: "power2.out" }, 2.0);
tl.fromTo("#cabeca", { x: 0 }, { x: corrida, duration: 9, ease: "power1.inOut" }, 2.0);
tl.fromTo("#titulo", { y: 50, opacity: 0 }, { y: 0, opacity: 1, duration: 0.8, ease: "power3.out" }, 4.0);
tl.fromTo("#apoio", { y: 30, opacity: 0 }, { y: 0, opacity: 1, duration: 0.7, ease: "power2.out" }, 7.5);
// from 11 s to 15 s the main frame holds (DESIGN.md rule: the ending holds for at least 1.5 s)
return tl;
};<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<link rel="stylesheet" href="abertura.css" />
<style>html, body { width: 1920px; height: 1080px; }</style>
</head>
<body>
<div id="root" data-composition-id="main" data-start="0" data-duration="15" data-width="1920" data-height="1080">
<div id="luz"></div>
<div id="bloco">
<p id="rotulo">Formação Cinema com IA</p>
<h1 id="titulo">Motion por código</h1>
<p id="apoio">HyperFrames e Remotion, do terminal ao MP4</p>
</div>
<div id="trilho"></div>
<div id="cabeca"></div>
</div>
<script src="abertura.js"></script>
<script>window.__timelines["main"] = window.criaAbertura();</script>
</body>
</html>K=~/projetos/formacao-cinema-ia-motion-v72/kit
export HYPERFRAMES_NO_TELEMETRY=1 NO_COLOR=1
E=${TMPDIR:-/tmp}/ensaio-motion/a8; rm -rf "$E"; mkdir -p "$E"
cp -r "$K/exemplos/a8-abertura" "$E/abertura"; cd "$E/abertura"
python3 "$K/conferir-design.py" DESIGN.md abertura.css; echo "saída: $?"
npx -y hyperframes@0.8.176 check 2>&1 | grep -E "Check passed|✗"
npx -y hyperframes@0.8.176 render --quality draft --workers 2 --quiet --output renders/abertura-16x9.mp4 > render-16x9.log 2>&1
npx -y hyperframes@0.8.176 render -c formatos/vertical.html --quality draft --workers 2 --quiet --output renders/abertura-9x16.mp4 > render-9x16.log 2>&1
python3 "$K/conferir.py" renders/abertura-16x9.mp4 15 1920x1080; echo "saída: $?"
python3 "$K/conferir.py" renders/abertura-9x16.mp4 15 1080x1920; echo "saída: $?"OK: 4 seções · 6 cores · 5 regras de movimento · a composição usa só cores do DESIGN.md saída: 0 ◇ Check passed OK: 15,0 s · 1920×1080 · 30 fps · sem áudio saída: 0 OK: 15,0 s · 1080×1920 · 30 fps · sem áudio saída: 0
The 9:16 render uses -c formatos/vertical.html. vertical.html is index.html with the root at 1080×1920, the vertical class, and the paths ../abertura.css and ../abertura.js.
✗ Output resolution incompatible outputResolution portrait (1080×1920) does not match the aspect ratio of the composition (1920×1080). The composition is landscape, so use --resolution landscape instead.
The --resolution option changes the resolution within the same aspect ratio (for example, 4K). Going from 16:9 to 9:16 means changing the framing: a different composition.
On the first render, the 9:16 broke the title into "Motion por" / "código": failure 2 from lesson 3. The fix was to tie the last two words together with in the HTML ("por código"). It only showed up by looking at the frames.
Trying to get the 9:16 out of the 16:9 composition with a render option.
export HYPERFRAMES_NO_TELEMETRY=1 NO_COLOR=1
cd ${TMPDIR:-/tmp}/ensaio-motion/a8/abertura
npx -y hyperframes@0.8.176 render --resolution portrait --quality draft --output renders/torto.mp4 2>&1 | grep -A2 "incompatible"On an ordinary machine, renders run one at a time. Run it, interrupt it and run it again: only what was left over goes back in the queue.
Browser simulation: nothing really renders. The failure is on purpose, to show that only the step that failed goes back.
The rehearsal, the error, and your opening.
Run the "brand, check, two renders and conferir" block.
Done when: OK: 15,0 s · 1920×1080 · 30 fps · sem áudio and OK: 15,0 s · 1080×1920 · 30 fps · sem áudio, each with saída: 0.
Run the --resolution portrait block.
Done when: ✗ Output resolution incompatible and the line that ends in use --resolution landscape instead.
In your project, with your DESIGN.md: ask the agent (RISE) for the 15 s opening in index.html and formatos/vertical.html, check the frames, render both, and run the checker on both.
Done when: K=~/projetos/formacao-cinema-ia-motion-v72/kit; python3 $K/conferir.py renders/abertura-16x9.mp4 15 1920x1080; python3 $K/conferir.py renders/abertura-9x16.mp4 15 1080x1920; python3 $K/conferir-design.py DESIGN.md abertura.css; echo "saída: $?" → two lines starting with OK: 15,0 s, one with OK: 4 seções and saída: 0.
Pick a start date and how much time you have per week.
The explanation appears after you answer.
Question 1 of 3
How do you get the 9:16 out of the same opening?
Question 2 of 3
Why does the choreography read the root's width (data-width)?
Question 3 of 3
The 9:16 render failed after the 16:9 was done. What do you redo?
Pick one option. The explanation appears after your attempt.