Case study · 2026 · Personal project

An automated YouTube channel for children: animation, voices, quality checks and approvals, orchestrated with n8n

Aksil the Tiger teaches English to four friends in one-minute episodes for children aged 3 to 6. Every episode is produced by a pipeline — script, voices, lip-synced animation, subtitles, thumbnail, upload — and nothing reaches YouTube until I approve the script and the finished video from my phone.

n8nAutomationRemotionReactText-to-speechSpeech recognitionYouTube Data APIPython
Role
Sole developer — curriculum, characters, animation engine, pipeline, orchestration
Stack
Python · Remotion (React) · Kokoro TTS · faster-whisper · Rhubarb · n8n · Telegram · YouTube Data API
Scale
101-episode curriculum · 91 board pictures + 8 scenes · 13 episodes produced · 2 per week
Status
Live channel, two episodes a week; uploads are reviewed in Telegram

What it does

Architecture

n8n is the conductor, not the worker. It never gets shell access: it calls a small local HTTP service that exposes a handful of fixed actions — next episode, produce, approve, reject, upload next — so a mistake in a workflow can't run arbitrary commands on the server.

An episode is a JSON script

Each episode is a list of lines (who speaks, what they say, pauses) with events pinned to words: show a picture on the board, write a word, start a "Your turn!" countdown, flag Zazi's mistake. A validator checks every script before anything is rendered — line count, target words actually spoken, events pointing at real words, only pictures that exist, YouTube fields within limits. Scripts can be written ahead in batches, so the Monday and Thursday runs never wait on anyone.

Voices, and a second listener

Each character is a Kokoro text-to-speech voice with its own speed and pitch. Then faster-whisper listens to every generated line and compares what it hears with what was written. That check is the reason the channel sounds right: it caught "parrot" heard as "parent", "doll" as "dial", "stop" as "Istapa". The same word timings drive the animation — a word appears on the board exactly when it is spoken, and Rhubarb turns the audio into mouth shapes for lip sync.

Animation as code

The characters are SVG rigs drawn in Python — heads, eyes, eyelids, mouths, arms and poses as separate parts — and Remotion renders the scene frame by frame in React from the timeline. The board pictures come from a library of 91 drawings and 8 backgrounds (classroom, race track, park, farm, beach, camping night, living room, party room). Every picture is checked by a script that measures it against the safe area of the board and renders it next to the class at real size, so nothing overlaps the chalk tray or looks too small.

Approvals and uploads

On Monday and Thursday at 9:00 n8n asks for the next episode and sends me the script with Produce / Reject buttons. If I approve, the video is produced (about four minutes on CPU) and sent back as a preview with Publish / Reject. Approved episodes wait in a queue; at 16:00 the next one is uploaded with its thumbnail, subtitles, chapters, playlists and localized titles, marked made for kids. Every outcome — uploaded, nothing to upload, an error, a login problem — ends as a Telegram message, and a warning appears when fewer than five ready episodes are left.

Bugs that taught me something

Short words came out garbled. The voice engine synthesizes each fragment between pauses on its own. After a suspense pause — "Red means… stop!" — a one-syllable word was spoken alone and came out as noise: "stop" was heard as "Istapa", "doll" as "dial". The speech-recognition check flagged it; I tested more than 60 alternative phrasings and kept only those heard correctly ("stop, stop!", "This is a doll!"). Lesson: design scripts for the engine, then verify with a second model instead of trusting your ears on a laptop speaker.

The checker lied twice. Whisper writes numbers as digits, so "two" looked missing and every counting episode was flagged — and the same mismatch made number words pop onto the board at the start of the line instead of when they were said. A name alias ("Yes" as a mishearing of "Yaz") silently swallowed every "Yes!". Both were fixed in one normalisation step shared by the check and the timing code. Lesson: a QA tool needs its own tests.

An unanswered approval deleted an episode. When n8n's "send and wait" times out, it passes the original input through, and my workflow read "no answer" as "rejected" — the episode dropped out of the curriculum without a trace. Now there is an explicit third path: no answer means keep it and ask again at the next run.

Child processes crashed only under the process manager. Rendering worked from a terminal but aborted under pm2: the Node child processes inherited pm2's IPC file descriptor variable and exited on start. Sub-processes now get a sanitized environment.

Result

The channel publishes two episodes a week from a written curriculum, with every picture, background and voice line checked before a child sees it, and a person approving every script and video. The same pipeline can produce the whole 101-episode plan: new episodes only need a script batch and two taps a week.

Keep reading