From script to an illustrated reel, at no cost
You write a roteiro.json; rtexjp returns a vertical 1080×1920 reel in navy blue, with an illustrated avatar of Nei, a robot mascot, Nei’s voice (or yours), and one request for an opinion: "comment PAGO or LOCAL".

rtexjp is an engine that assembles short vertical illustrated videos (reels) in navy blue from a script written in a text file (roteiro.json). It is the engine Nei uses for INEMA reels, open to anyone who wants to study or adapt it: no need to record yourself or pay for an avatar service. It generates the voice, draws each scene frame by frame, mixes the soundtrack, and checks on its own that the video follows the format. You need a computer with a graphics card (GPU), Python, Node, ffmpeg, and two local servers: one for voice (inemavox) and one for images (flux2-klein).
It comes configured with Nei’s voice and avatar. To use your own, point the environment variables RTEXJP_VOZ_REF (voice reference recording) and RTEXJP_AVATAR_REF (avatar photo) to your files, describe your character in RTEXJP_AVATAR_DESC (otherwise the poses will have Nei’s cap, glasses, and beard), and generate the poses again; you don’t need to edit any code. These two files are not included in the repository (see Prerequisites).
A fixed format checked by a validator
Every reel follows the same structure: a hook in frame 0, proof with a source, the viewer’s problem, numbered points, an honest caveat, a maxim, and an opinion question. The engine draws it; the validator flags anything that falls outside the format.

🎨 Illustrated, no lip sync
A large illustrated avatar of Nei at the bottom (≈ 43 % of the height), in 11 poses tied to the emotion of the speech (startled, unsure, pointing, explaining, thumbs-up…), one hero illustration per scene, and a robot mascot. No one needs to record anything.
🎙️ Nei’s voice, local
Voice generated with local TTS (inemavox chatterbox), at 160–190 words per minute. Local Whisper marks the timing of each word for captions and scene triggers.
✅ Automatic QA
Measures duration, speech pace, volume (−14 LUFS), visual changes per minute, proof within 20 s, options planted before the prompt, CTA without a delivery promise, final card, and silence before the maxim. It produces a qa.md and a contact.jpg.
The nine rules of the rtexjp standard
These are the rules enforced by the checar command and QA for every edit.
- A hook in frame 0, of one of these types: contrary (tells people to stop doing something everyone does), loss (what if X disappeared?), fear statistic with a source, or a third-party story with a number. Not allowed: lists ("5 things…"), "did you know…", "I did…".
- Frame 0 headline of up to ~6 words, in uppercase, with one word in yellow, more forceful than the speech. Draw an object with visible damage (scratched in red, broken); the avatar reacts in the first second, and something changes every ~1 s in the first three seconds. Frame 0 serves as the cover.
- Proof within 20 s: a real screenshot of the source page, the number in the headline, and the
fonte:line below. - The viewer’s problem, with reassurance ("it’s not your fault; no one gave you the map"). The final question is planted here: the options appear in yellow.
- One topic per video, in 2–5 points with a visible index and a pill bar at the top; one object per sentence, changing every 2–3 s.
- Every number has a written source on screen (
fonte: …). - An honest caveat, spoken aloud, on an alert card (red = problem, green = correct).
- A maxim in antithesis, after 0.8 s of real silence (voice and soundtrack stop).
- An opinion CTA: Nei states his side and asks a strong question that can be answered with one word ("comment PAGO or LOCAL"). The card shows the question and options; the comment box types one option, posts it, and confirms it. It stays for ~10 s, with no sign-off. Never promise to deliver anything ("I’ll send it to you," "I’ll send you"): no one replies to comments. The link and source stay in the pinned comment.
Quick glossary
Opinion prompt
The question at the end, with two one-word options (in the 1st video, PAGO or LOCAL). The tension appears in frame 0, the options are planted early, and only those (and the headline’s hot word) are highlighted in yellow.
Maxim
The summary line at the end, in antithesis ("Being the owner of your system was never about knowing every line. It’s about knowing what holds what in place."). It comes after a short silence.
Stage
The motor/palco.html page that draws each scene. The renderer takes a snapshot of it for every frame.
Music bed
Quiet music under the voice that gets quieter when the voice speaks and stops during the silence before the maxim.
LUFS
A measure of perceived loudness. −14 LUFS is the volume used as a standard by social platforms.
CTA
"Call to action": the video’s final request. Here it is always a single opinion question answered with one word.
TTS and lip sync
TTS is a voice generated from text. Lip sync is mouth movement matching the speech; rtexjp does not do that, the avatar only changes poses.
Whisper and Playwright
Whisper transcribes the voice and times each word. Playwright opens the stage in a headless browser and takes a snapshot of each frame.
Typography flash
A quick screen with only large text between points, so the video doesn’t feel static.
Block types
tese = claim (the hook, the problem); autoridade = proof with a source; virada = the index that introduces the points; metodo = each point; planta is just the id of the block that plants the options for the final question.
Beat
A section of a block that changes the headline, illustration, and pose on the word marked in em. It makes something change every 2–3 s.
Hero illustration
The large illustrated object in the middle of the screen (broken terminal, map, padlock), taken from the motor/assets/objetos/ library.
The v1.2 look, from top to bottom
- Label in monospace (≥ 30 px) and, for the points, pills 01–03.
- Headline in Sora ExtraBold, centered, 2–3 lines, ≥ 96 px, almost white, with one word in light steel blue (yellow only in frame 0).
- Hero illustration or real screenshot, large, between the headline and avatar.
- Karaoke captions in their own strip, with the spoken word highlighted.
- Large avatar, at the bottom and centered, in the beat’s pose; the face stays above 80 % of the frame height.
The background has a spotlight and light grain and alternates between navy and ice-blue paper ("tema": "claro" in the scene), with navy text. During the silence before the maxim, the last frame freezes with the avatar looking thoughtful: the screen is never blank.
Visual quality gate: in two samples per block, QA measures the headline (≥ 96 px), all text (≥ 30 px), occupied area (≥ 55 % of the usable frame), text contrast (≥ 4.5:1), and avatar height (≥ 38 %). Applies to scripts with "versao": "rtexjp/3".
Six steps, one command each
rtexjp.py runs the steps in order. The stage is an HTML page drawn by window.render(t): the same build/cena.json always produces the same video. The voice does not: TTS comes out a little differently each time (so the voz step lets you redo just a few blocks).
Script and validation
checar checks the structure using the tipo field: it starts with tese, has a virada before the first metodo (2–5 points), a ressalva before the maxima, and the maxima immediately before the final cta. The 2nd example uses tese (hook) → autoridade (proof) → tese (problem + options) → virada → metodo ×3 → ressalva → maxima → cta.
Voice and scene
voz generates one audio file per block, speeds it up to 1.15×, and joins them; cena combines the script with the timing of each word.
Render, mix, and QA
render snapshots the stage frame by frame; mix adds the soundtrack (which stops during the maxim’s silence); qa measures everything.
What needs to be on your machine
Everything runs locally. No paid APIs are called. Paths come from environment variables, with Nei’s machine as the default; if your setup is different, export the variables (table below) and you don’t need to change any code.
Voice: inemavox + chatterbox environment
Clone the inematds/inemavox project to ~/projetos/inemavox (the engine calls its tts_direct.py and whisper_daemon.py). Chatterbox TTS and Whisper large-v3 run in a separate Python environment (on Nei’s machine, the conda env chatterbox at ~/miniconda3/envs/chatterbox); if yours is elsewhere, point the variables below to it. If you clone it somewhere else, export RTEXJP_INEMAVOX with the path. Whisper large-v3 uses about 10 GB of GPU memory (NVIDIA), plus chatterbox. Tested on Linux.
git clone https://github.com/inematds/inemavox ~/projetos/inemavox cd ~/projetos/inemavox && python3 -m venv venv ./venv/bin/pip install -r requirements.txt # o rtexjp chama venv/bin/python tts_direct.py # chatterbox + faster-whisper num ambiente próprio (ver o README do inemavox) export CHATTERBOX_PYTHON=/caminho/do/env/bin/python3 # TTS export CS_WHISPER_PY=/caminho/do/env/bin/python3 # Whisper
Image: flux2-klein
The inematds/inemaimg server with the flux2-klein model (default http://localhost:8000/generate, or the address in RTEXJP_IMG_URL), plus the avatar reference photo (RTEXJP_AVATAR_REF). Needed only for optional steps 4 and 5 (poses and scene illustrations); Nei’s poses and the illustration library are already included.
Node, ffmpeg, Python
Node 18 or newer, ffmpeg (includes ffprobe), and Python 3 with Pillow and numpy.
git clone https://github.com/inematds/rtexjp cd rtexjp npm i npx playwright install chromium pip install --user pillow numpy # ou dentro de um venv
Paths and environment variables
RTEXJP_INEMAVOX · inemavox folder (TTS + Whisper) | ~/projetos/inemavox |
RTEXJP_VOZ_REF · voice reference recording (.wav) | ~/projetos/timesmkt3/media/voice-refs/nei.wav |
RTEXJP_IMG_URL · flux2-klein image server | http://localhost:8000/generate |
RTEXJP_AVATAR_REF · avatar reference photo | ~/projetos/inema-referencias/nei-avatar.jpg |
RTEXJP_AVATAR_DESC · character features and clothing in the image prompt (in English) | black cap, glasses, gray beard, steel blue T-shirt (Nei) |
RTEXJP_TRILHA · mix music bed | motor/assets/trilha.mp3 |
CS_WHISPER_PY · Python for the faster-whisper environment (Whisper server and fallback) | ~/miniconda3/envs/chatterbox/bin/python3 |
CHATTERBOX_PYTHON · Python for the chatterbox environment, required by inemavox tts_direct.py | the one in the chatterbox environment |
# example: everything at different paths, no code changes export RTEXJP_INEMAVOX=~/inemavox export RTEXJP_VOZ_REF=~/my-voice.wav export RTEXJP_AVATAR_REF=~/my-photo.jpg export RTEXJP_AVATAR_DESC="same face, short brown hair, no glasses. She wears a plain navy blazer" export RTEXJP_IMG_URL=http://outra-maquina:8000/generate
From script to final.mp4
Each edit lives in edicoes/<id>/. The tudo shortcut (step 10) runs checar, voice, scene, render, mix, and QA in one go (without the frame preview from step 7).
Set your voice reference you
The voice step clones the voice from a short recording. The default points to Nei’s recording, which is not included in the repository; without setting your own, the voz step fails. Record 10 to 20 seconds of clear speech in WAV format and export RTEXJP_VOZ_REF. If you are also changing the avatar, export RTEXJP_AVATAR_REF with a photo of yourself and RTEXJP_AVATAR_DESC with your features and clothing, then run step 4.
export RTEXJP_VOZ_REF=~/minha-voz.wav export RTEXJP_AVATAR_REF=~/minha-foto.jpg # só se for trocar o avatar
Write the script you
Copy the example script and replace its content. The format is summarized just below the guide, in "The roteiro.json file".
mkdir -p edicoes/minha-edicao cp edicoes/2026-10-28-china-fecha/roteiro.json edicoes/minha-edicao/ # depois edite
Validate the format rules
No taste judgments, just rules. It rejects: incorrect block order; fewer than 2 or more than 5 points; an index that doesn’t match the number of points; digits or exclamation marks in speech (spell out numbers); a fonte that does not exist in fontes; a word in destaque_fala or a trigger that is not in the speech; a number on screen without a source; silence before the maxim outside 0.5–0.9 s. Hook: gancho.tipo not in the list; list/"did you know"/"I did" hooks; a frame 0 headline with more than ~6 words, no uppercase, or no hot word; no damaged object; proof after the 3rd block. Prompt: question without "?"; options that are not a single uppercase word; an option missing from the CTA speech or not planted earlier; Nei’s side not stated before the question; a deliverable; or a CTA promising delivery ("I’ll send it to you," "I’ll send you," "comment X and…"). Scripts in the old format (keyword + deliverable, without a hook) pass with a LEGACY warning only.
python3 rtexjp.py checar edicoes/minha-edicao
Avatar poses optional
Nei’s 11 poses (steel blue T-shirt) are already in motor/avatar/: susto, surpreso, duvida, queixo, apontando, olha, explica, polegar, bracos, segurando, mostrando. Run this only if you changed the character: the photo (RTEXJP_AVATAR_REF) and the description of features and clothing (RTEXJP_AVATAR_DESC, in English, included in the flux2-klein prompt). Pass pose names to regenerate only those. Check each pose before continuing: the green background is removed automatically, but a distorted hand or dark background means you should try again.
python3 motor/gerar_avatar.py # todas python3 motor/gerar_avatar.py susto duvida # só essas
Hero illustrations and proof screenshot optional
Each beat in the script shows a large illustrated object in the same flat style, between the headline and avatar. They live in a reusable library, motor/assets/objetos/<nome>.png, with the prompt and seed in indice.json. 18 are already included (broken terminal, map with pins, torn map, magnifying glass over files, speech bubble, dominoes, cost alert, folder, key, padlock, locked faucet, desktop computer, stored hard drive, iceberg, price tag, meter, balance scale, and empty wallet). To create a new one, describe the object in English; if it comes out distorted or blotchy, change the seed. Proof uses a real screenshot of the source page, stored in edicoes/<id>/prova/ and referenced in the beat’s print field.
python3 motor/gerar_objeto.py foguete "a small blue rocket taking off" 11 python3 motor/gerar_objeto.py --lista node motor/capturar_prova.mjs https://github.com/dono/repo edicoes/minha-edicao/prova/pagina.png node motor/capturar_prova.mjs https://github.com/dono/repo edicoes/minha-edicao/prova/estrelas.png --clip 1130,180,130,44
Voice for each block + timing for each word
One audio file per block with Nei’s voice; Whisper marks each word in build/tempos.json. Pass block names to redo only those. Whisper runs as the inemavox server (whisper_daemon.py, using the Python from CS_WHISPER_PY); if it does not start, the engine uses inemavox’s transcrever_v1.py with the same Python. The log is in ~/.cache/whisper_daemon.log.
python3 rtexjp.py voz edicoes/minha-edicao # todos os blocos python3 rtexjp.py voz edicoes/minha-edicao p2 ressalva # só esses
Build the scene and check individual frames
cena writes build/cena.json. Before the full render, take a few frames (in seconds) to review.
python3 rtexjp.py cena edicoes/minha-edicao node motor/render.mjs edicoes/minha-edicao/build --quadros 0,10,20 # -> edicoes/minha-edicao/quadros/
Render at 1080×1920 and 30 fps
Playwright snapshots the stage frame by frame, and ffmpeg combines them into build/video.mp4 (without sound).
python3 rtexjp.py render edicoes/minha-edicao
Mix and QA
Voice + quiet soundtrack (which stops during the maxim’s silence), with final volume at −14 LUFS. The track in motor/assets/trilha.mp3 is the music bed for Nei’s reels: to publish your own, point RTEXJP_TRILHA to music with a license you know; then QA writes qa.md, build/qa.json, and contact.jpg. Any failed criterion appears with ❌ in qa.md, and the command ends with a Python error (CalledProcessError): that means QA failed, it is not a bug.
python3 rtexjp.py mix edicoes/minha-edicao # -> final.mp4 python3 rtexjp.py qa edicoes/minha-edicao # -> qa.md, contact.jpg
Or run everything at once
Runs checar → voice → scene → render → mix → QA. Stops if checar fails.
python3 rtexjp.py tudo edicoes/minha-edicao
Review before publishing you
Open contact.jpg and watch the video. The title, description (ending with the question), and pinned comment (with the repository link and source, simply listed, not offered as a reward) are in the script’s publicar block. Publishing is outside this repository.
The script format at a glance
An object with id, versao, titulo, gancho, isca, fontes, the blocos list, and the publicar block. A shortened excerpt from the 1st real example (the // lines are not valid JSON; the full file is at edicoes/2026-10-28-china-fecha/roteiro.json):
{
"id": "2026-10-28-china-fecha", "versao": "rtexjp/3",
"gancho": { "tipo": "perda", "objeto": "torneira cromada fechada com cadeado vermelho..." },
"isca": { "tipo": "opiniao", "pergunta": "Se a torneira fechar, você paga ou roda local?",
"opcoes": ["PAGO", "LOCAL"], "lado": "Eu já rodo a minha aqui em casa" },
"fontes": [ { "id": "mofcom", "valor": "pela 1ª vez, modelos grandes de IA na lista de exportação controlada",
"linha": "Recode China AI · 21/10/2026 · sobre o rascunho de 16/10" } ],
"blocos": [
{ "id": "tese", "tipo": "tese", "pose": "susto",
"fala": "E se a China fechar a torneira da IA grátis? O modelo barato que você usa hoje pode sumir.",
"tela": { "texto": "A CHINA FECHA A TORNEIRA?", "destaque": "FECHA" },
"cena": { "comp": "quadro", "params": { "beats": [
{ "manchete": ["A CHINA", "FECHA A", "TORNEIRA?"], "destaque": "FECHA A",
"objeto": "torneira_fechada", "carimbo": "LICENÇA", "pose": "susto" },
{ "em": "sumir", "manchete": ["PODE", "SUMIR"], "objeto": "carteira_vazia", ... } ] } } },
// ... autoridade (prova com print), tese (problema + opções), virada, metodo ×3, ressalva ...
{ "id": "maxima", "tipo": "maxima", "silencio_antes_s": 0.8, "cena": { "comp": "maxima", ... },
"fala": "IA grátis nunca foi sua. Era emprestada. Sua é a que roda na sua máquina." },
{ "id": "cta", "tipo": "cta", "hold_s": 7.5, "cena": { "comp": "opiniao", "params": { "digita": "LOCAL" } },
"fala": "Eu já rodo a minha aqui em casa. E você, se a torneira fechar: paga ou roda local? Comenta PAGO ou LOCAL." }
],
"publicar": { "titulo": "...", "descricao": "... termina na pergunta?", "comentario_fixado": "fonte + pergunta" }
}
Fields in each block
tipo (order above) · fala (spelled out, no digits) · fala_tts (optional, spelling to help TTS pronounce it correctly) · tela (large text and highlighted word) · cena.comp + cena.params (what the stage draws; params.gatilhos ties a scene change to a word in the speech) · avatar pose · destaque_fala (colored caption words) · fonte (id in fontes) · rotulo and n (point number). Only in maxima: silencio_antes_s (0.5–0.9). Only in cta: hold_s, how long the card stays after the last word (5–9 s; with the speech, the whole card lasts ~10 s). At the top level: gancho.tipo (contrario, perda, estatistica, historia) and gancho.objeto; isca with tipo: "opiniao", pergunta, opcoes, and lado.
Stage components (cena.comp)
Current format: gancho (frame 0 with damaged object), contador (supports decimais), duas (problem + prompt options), indice, mapa, explica, impacto, ressalva, flash, opiniao (question card). From the 1st edition, still on the stage: titulo, frase, pasta, repo, carro, equipe, cta (legacy). The robot mascot is drawn by the stage itself, beside the avatar, and reacts to the scene (question mark, alert, check mark). Poses: bracos, queixo, apontando, segurando, surpreso, mostrando. Each component’s parameters are in the examples edicoes/2026-10-28-china-fecha/roteiro.json and edicoes/2026-10-28-paga-200/roteiro.json.
Two videos made with the engine
One topic in two parts: what if China stopped providing free AI and paid AI jumped from $200 to $5,000? The 1st video focuses on China; the 2nd, on the price. Both follow the current format (navy blue, v1.2 visuals, hook, proof with a real source screenshot, honest caveat, and opinion question). The numbers are treated as a scenario: the Chinese draft is not law yet, and $5,000 is an estimate; each video says so in the honest caveat.
1st video · "E se a China fechar a torneira da IA grátis? (What if China turned off the free AI tap?)" loss hook
The proof is China’s draft export control published on 10/16/2026: for the first time, large AI models are added to the list of items that can leave the country only with a license. Three steps (download, run, separate) and the question PAGO (PAID) or LOCAL. Files in edicoes/2026-10-28-china-fecha/ (the script’s publicar block includes the title, description, and pinned comment).
What QA measured
| Duration | 79.7 s |
| Resolution | 1080×1920 · 30 fps |
| Speech rate | 174 words/min |
| Volume | −14.0 LUFS |
| Visual changes | 24.8 per minute |
| Headline / smallest text | ≥ 98 px / ≥ 30 px |
| Area occupied / avatar | ≥ 55% / 43% of height |
| Text contrast | ≥ 5.2:1 |
| Proof (fact with source) | at 6.6 s |
| Poll options before CTA | 47.7 s before |
| CTA without delivery promise | yes |
| Silence before the takeaway | 0.85 s |
| End card (spoken + held for 7.5 s) | 14.6 s |
The player plays edicoes/2026-10-28-china-fecha/final.mp4 from the repository. Download final.mp4 (21 MB)
Frame by frame (1st video)








The 1st video’s contact sheet

2nd video · "Você paga 200 dólares. A IA custa até 5 mil. (You pay $200. AI costs up to $5,000.)" number hook
The proof is an internal Cursor analysis, published by Forbes and summarized by the-decoder on 03/07/2026: the $200 Claude Code plan can consume up to $5,000 worth of computing per month. Three steps (measure, separate, plan B) and the question PAGA (PAY) or SAI (QUIT). Files in edicoes/2026-10-28-paga-200/.
What QA measured
| Duration | 74.2 s |
| Resolution | 1080×1920 · 30 fps |
| Speech rate | 175 words/min |
| Volume | −14.0 LUFS |
| Visual changes | 22.7 per minute |
| Headline / smallest text | ≥ 96 px / ≥ 30 px |
| Area occupied / avatar | ≥ 56% / 43% of height |
| Text contrast | ≥ 5.2:1 |
| Proof (number with source) | at 6.0 s |
| Poll options before CTA | 59.7 s before |
| CTA without delivery promise | yes |
| Silence before the takeaway | 0.84 s |
| End card (spoken + held for 7.5 s) | 14.6 s |
The player plays edicoes/2026-10-28-paga-200/final.mp4 from the repository. Download final.mp4 (20 MB)
Frame by frame (2nd video)








The 2nd video’s contact sheet

Where the project stands
What is currently in the repository.
checar, opinion prompt without a deliverable, paths set through environment variables.motor/assets/objetos/), real screenshot for proof, centered Sora headline, karaoke captions, ice-blue paper alternating with navy, grain and spotlight, and the QA visual quality gate.