# The Godot Course I Wanted Was in Spanish: Why I Built This Instead of Buying Dubbing

> Source: <https://dev.to/pheomenon/the-godot-course-i-wanted-was-in-spanish-why-i-built-this-instead-of-buying-dubbing-49d9>
> Published: 2026-08-12 13:54:06+00:00

The best Godot course I could find was in Spanish. I don't speak Spanish.

Subtitles are the obvious answer and they do not work here. For a talking head they are fine, your eyes are free. In a screencast your eyes are the whole point: the instructor says "now drag this onto the node" while his cursor is already doing it, and you cannot read the bottom of the frame and watch his hands at the same time. I rewound a lot. I quit around lesson four.

So I went to price dubbing it instead. The course runs about six hours.

Figures below are from Rask's and HeyGen's own pricing pages on 12 August 2026. I checked the pages rather than the blog posts about them, because two of the "2026 pricing" articles I found first were already wrong: one had HeyGen's Creator allowance at a third of what the site says.

[Rask](https://www.rask.ai/pricing) sells minutes. Creator is $60/mo for 25 minutes, Creator Pro $150/mo for 100 minutes, Business $750/mo for 500. Six hours is 360 minutes, so on Creator Pro that is four months of subscription, about $540, to watch one course once.

[HeyGen](https://www.heygen.com/pricing) sells credits. Creator is $29/mo for 600 credits, Pro $49/mo for 1,000, and audio dubbing without lip sync costs 2 credits a minute. Those 360 minutes are 720 credits, so about $49 on the right plan. That is a fair price and I would rather say so than pretend the whole market is a rip-off.

What stopped me was not the top of the range. It was that every free tier is a demo: three minutes on Rask, three watermarked videos on HeyGen. There was no version of "I am one person, I want to watch one course, on my own laptop, and I would rather not pay a subscription for that."

My laptop can run text-to-speech. Paying rent to hear a course in my own language felt like the wrong shape of problem.

I built [Dub Any Video](https://dubanyvideo.com) to do it, and the shape of the thing is six boxes, not one model:

```
  video ──► extract audio ──► transcribe (Whisper, word timings)
                                    │
                                    ├──► who is speaking?  (server mode only)
                                    ▼
                              translate lines
                                    ▼
                              speak each line  (Piper on your device,
                                    │           or 10 voices on servers)
                                    ▼
                    fit the speech back into the original timing
                                    ▼
                          dubbed audio + subtitles
```

Each box takes the previous box's output as true, which is where quality goes to die.

I assumed boxes two and four were the hard ones. They are not. Whisper hands back six hours of Spanish more accurately than I would type it, and machine translation of technical narration is fine once the terms are fixed.

The last box is where the project actually lives, and it is the one I completely underestimated.

A line that takes four seconds in Spanish wants five and a half in mine. It still has to land in a four-second hole, because at second eight the instructor clicks something and starts talking about what he just clicked.

Three levers, in this order.

**Speed the speech up.** There is a ceiling around 1.3x. Past that it stops sounding like someone talking quickly and starts sounding like a hostage statement. It also has to be a real time stretch, which tripped me up for an embarrassing afternoon: `AudioBufferSourceNode.playbackRate`

resamples, which raises pitch, so my narrator became a chipmunk. FFmpeg's `atempo`

filter stretches without touching pitch, which is why an entire ffmpeg build now runs inside the browser tab.

**Slow the video down**, slightly, and only across the region that overflows. Below about 0.8x the picture reads as broken, and I tried lower before I believed it.

**Let it spill.** If a line still does not fit, it runs past its window into the gap after it. Audio is never truncated, because a cut sentence is worse than a late one, and tutorials are full of gaps where the instructor is typing.

That cascade is most of the engineering. The AI part is a few calls around it.

A translation engine has no memory of what it decided ten seconds ago. Run a Godot course through one and `Node`

, `scene`

, `signal`

and `viewport`

come back as three or four different words across the lessons, while `AnimationPlayer`

gets helpfully translated when it is a class name that has to survive untouched.

That is not a knowledge problem, so a better model does not fix it. You fix it by deciding the words once and applying them everywhere, and by reading the transcript and the translation side by side before a single line is spoken. It is why I put a review screen in front of generation instead of a progress bar: I wanted the boring pass to be unavoidable, including for me.

*A wrong word here costs one keystroke. The same word after generation costs a re-render. (Click for the full screen.)*

This is the mode I built for myself, and there is no meter on it. Not "unlimited within a fair use policy", not a trial that expires. There is nothing to meter, because your machine synthesizes the speech, so there is no per-minute cost for me to pass on. No card is asked for.

The video goes into your browser's own storage and stays there. Speech synthesis is Piper compiled to WebAssembly, running on your device, using WebGPU where the browser exposes it. A voice model is downloaded once from its public repository and reused by every later lesson, which is why the first lesson in a new language is slow and the tenth is not.

Two things do leave, and I would rather spell them out than write "100% private" and hope nobody opens the Network tab. If you ask for a transcript, the audio goes from your browser straight to a speech-to-text provider on your own API key, never through me. If you translate with the free option, the text of your lines goes to a public translation endpoint. Import an SRT and a translation you already have, and neither happens: nothing leaves at all.

It speaks 39 languages here against 31 on my servers, because the on-device catalogue and the server one are genuinely different sets. Catalan, Welsh, Persian and Georgian exist only in the browser.

Rendering resumes per line. Every synthesized line is written to storage the moment it exists, together with a hash of the text it came from, so closing the tab loses nothing and editing one line later re-renders that line instead of the lesson. I wrote that after losing a 40-minute render to a stray Cmd-W.

*The free mode being honest on screen: on-device voices offered, premium ones listed as unavailable rather than hidden, and the one-voice limit stated instead of buried. (Click for the full screen.)*

Out the other end you get a WAV track and an SRT per language, or the whole course as one archive.

The limits are real ones. The tab has to stay open, it does one lesson at a time, and the whole lesson is read by a single voice, because working out who is speaking runs server-side. There is no course glossary here either. A long lesson warms the laptop up, and a phone is the wrong machine for this.

Everything above assumes you are me, watching one course. A team localizing a catalogue has a different problem, and that is what the server side is for.

It separates speakers, giving each person their own voice out of ten. That matters the moment a course has an interviewer and a guest: one voice for two people is noticed immediately, even by someone who cannot say what is wrong.

Jobs outlive the tab, so forty lessons in six languages is a thing you start and then close the laptop on. Every lesson and language pair carries its own state on one screen: done, generating, stale after an edit, failed.

*One lesson is being voiced on the servers, two are waiting for a human, and the line under the bar says you can close the tab. That last part is the actual difference between the two modes. (Click for the full screen.)*

The glossary lives here too: decide once, at course level, that `AnimationPlayer`

stays `AnimationPlayer`

, and every lesson in every language obeys. Same for per-language voice defaults.

It speaks 31 languages, 26 shared with the browser list. Croatian, Japanese, Korean, Estonian and Lithuanian exist only on this side. One job is capped at an hour of media, results are files with a stated expiry rather than a vague promise, and there is an API if you would rather drive it from code.

Pricing is a ladder: €7.99 a month for 8 hours of processing, €14.99 for 20, €29.99 for 50. The first tier is about €1 per hour of source video.

That is roughly a hundredth of the per-minute rates I quoted at the top, and it is not because I am clever. It is because I skipped lip sync and voice cloning. Those are most of what you pay for elsewhere, they are genuinely expensive to run, and for watching a course neither one matters to me: I do not care whether the instructor's mouth matches, I care whether I understand him while I watch his hands.

Lips do not match, and will not in this design. On-screen text stays in the original language, so a Spanish editor UI stays Spanish. Voices come from a fixed set rather than from the original speaker. Neither mode translates live while you watch.

If you need a marketing video where the presenter's mouth moves correctly in nine languages, buy one of the tools I priced at the top. They do that well, and I do not do it at all.

I watched the Godot course in my own language in the end. Getting the class names right took about twenty minutes of reading per lesson, which is roughly the time I used to spend rewinding. [The browser mode is here](https://dubanyvideo.com/free-private-video-dubbing) if you have a course sitting in a language you do not speak; it does not ask for a card, and you can watch the Network tab while it runs.
