How I make AI videos with my own GPUs: 3090 vs 4090, first results

A fully self-hosted pipeline: script to voiceover to render, no cloud in the path. Two mascots, worst-to-best, the 3090/4090/DGX numbers, and why the right model depends on your character.

by Attila Macskásy 19 min read

I am pretty good with local AI when it comes to coding. I build projects and full-stack web applications with my own coder running fully on-premises, and have done for some time. Writing articles works too, because the local context is there — though sometimes an external model like Fable 5 adjusts the language, and for Hungarian content Fable still is not enough. I need to hire someone to do the final touch on the text.

Kolli the yellow octopus and Hörcsi the plush hamster standing together, a film strip spiralling around them, GPU server racks glowing on both sides

Even this article's feature image was generated in-house — on one of my own RTX 3090s, in about three and a half minutes. Recipe at the bottom.

Recently I had unlimited fun with image generation while building openchef.hu and later openchem.hu. That taught me how important it is to have a good text model before you run image rendering on AI.

Then, because of the clear feedback on nextpost.blog, I spent the whole weekend running AI video generation on my local AI fleet: 3090 vs 4090 vs DGX, different models, and two characters — Kolli (a drawing) and Hörcsi (a photo-realistic cartoon hero, actually from the miscellaneous toys section at Jysk).

Like I said, I am new to video. I have never done this before. And obviously, just as with coding, image and voice, I will compare the results against upstream cloud services — Google Veo 3.1 quality, Google AI Plus at 1890 Ft/month.

Where it ended up

Start with the result, because the rest of the article is how I got here. Kolli, our octopus logo, waving and introducing himself:

Motion generated from our existing logo artwork by an open image-to-video model, voice by an open text-to-speech model on the same box, assembled with ffmpeg. Nothing left the building.

Prompt, model and settings
  • Video model: Wan 2.2 I2V-A14B, fp8 — a two-stage graph (high-noise pass, then low-noise), ~14 GB of weights each · text encoder umt5-xxl fp8 · Wan 2.1 VAE
  • Hardware: 1× RTX 4090, 24 GB — 583 s
  • Settings: 704×704, 81 frames @ 24 fps, seed 7, 20 steps split 10/10, euler/simple, cfg 3.5, shift 8
  • Start frame: the logo artwork pasted onto a larger white canvas at 70 % of frame height, then mirrored — framing and wave-side are fixed here, never in the prompt
  • Voice: Piper, Hungarian female voice, CPU-only, 4.0 s, saying the line our helpdesk agent already says
  • Assembly: ffmpeg — horizontal flip, ×1.46 retime with motion interpolation to 30 fps, audio delayed 0.45 s, loudnorm

Prompt:

A cheerful cartoon octopus mascot with a bright yellow body and a royal blue knitted beanie stands centered on a clean white background. He has two large round eyes with black pupils and a completely blank smooth face with no mouth and no nose. He raises one tentacle high up beside his head and waves it smoothly from side to side several times in a big friendly greeting, a clear flowing waving motion. His body and all other tentacles stay calm and still. Flat 2D vector cartoon, thick dark outlines, brand mascot illustration, smooth animation, static camera, constant character size, plain white background.

Negative prompt:

both arms raised, arms up, flailing, mouth, mouth line, smile, lips, teeth, nose, blank eyes, missing pupils, blurry, low quality, watermark, text, letters, extra limbs, extra tentacles, deformed, distorted, melting, 3D render, photorealistic, realistic octopus, camera pan, camera zoom, zoom in, scale change, background change, color shift

Where it started

Here is the first version I was happy with, a day earlier. Watch the tentacle:

The same character, same start frame, same voice — the small model. It waves more, but it jitters.

It flickers. The body twitches under the waving tentacle and the motion stutters rather than flows. I only really saw it once someone else watched it.

You can measure that difference rather than argue about it. Averaging how much each frame changes from the one before gives you how much motion there is; averaging how much that number jumps around gives you how erratic it is — which is what the eye reads as flicker:

Kollimotionerraticness
Small model (first version)1.641.283
Large model (final)0.980.294

4.4× smoother. Note the large model actually moves less — and looks far better. That was the single most useful thing I learned all weekend, and I get to it below.

Prompt, model and settings
  • Video model: Wan 2.2 TI2V-5B (image-to-video), fp16 · text encoder umt5-xxl fp8 · Wan 2.2 VAE
  • Hardware: 1× RTX 3090 — 100.8 s (the same render is 48.8 s on a 4090)
  • Settings: 704×704, 81 frames @ 24 fps, seed 7, 20 steps, euler/simple, cfg 5, shift 8
  • Start frame: our existing logo artwork on a larger white canvas at 70 % of frame height — framing is fixed here, never in the prompt
  • Voice: Piper, Hungarian female voice, CPU-only, 4.0 s of audio, saying the line our helpdesk agent already says
  • Assembly: ffmpeg — ×1.46 retime with motion interpolation to 30 fps, audio delayed 0.45 s, loudnorm

Prompt:

A cheerful cartoon octopus mascot with a bright yellow body and a royal blue knitted beanie stands centered on a clean white background. He has two large round eyes with black pupils and a completely blank smooth face with no mouth and no nose. He raises one tentacle high up beside his head and waves it from side to side several times in a big friendly greeting, a clear repeated waving motion. He blinks exactly once near the beginning, then keeps his eyes wide open for the rest of the clip. Flat 2D vector cartoon, thick dark outlines, brand mascot illustration, smooth animation, static camera, constant character size, plain white background.

Negative prompt:

mouth, mouth line, smile, lips, teeth, nose, facial marking between the eyes, blank eyes, missing pupils, blurry, low quality, watermark, text, letters, extra limbs, extra tentacles, deformed, distorted, melting, 3D render, photorealistic, realistic octopus, camera pan, camera zoom, zoom in, scale change, background change, color shift

The whole path, with no cloud service anywhere in it:

existing logo artwork  →  open image-to-video model (ComfyUI, one GPU)  →  motion
Hungarian script       →  open text-to-speech model (CPU, same box)     →  voice
                       →  ffmpeg: retime, mux, encode                   →  final MP4

Four pieces, all open weights, all on machines I own. The voice is not a new one either — it is the same model that already speaks for our helpdesk agent, so the video says the sentence the phone line and the web chat already say.

The goal here was animation, not cinema. I was not trying to generate a drone shot over a forest. I was trying to make an existing brand asset — a registered trademark — move without changing. That is the harder problem: a generated landscape has no wrong answer, a mascot has exactly one.

3090 vs 4090

Same clip, same model, same seed, same ComfyUI build, same prompt. Only the GPU differs.

RTX 3090RTX 4090
Render time100.8 s48.8 s
Speed-up2.07×
GPU power while rendering340 W449 W
Energy per clip9.4 Wh5.6 Wh
Peak VRAM~22.4 GB22.7 GB
Peak temperature79 °C69 °C

The 4090 draws more watts but finishes in less than half the time, so it uses 40% less energy per clip and runs 10 °C cooler. Both cards are 24 GB, and both sat at nearly 23 GB — the memory is the constraint, not the speed.

Worth noting: on text generation I measured the 4090 at only 1.24× the 3090 for single-stream decoding. Video is compute-bound end to end, with no memory-bandwidth-limited decode phase to hide behind, so the gap is much wider here. If you are choosing a card for video specifically, that 2× is real in a way it is not for chat.

Same seed on both cards produced near-identical output (mean difference 0.5/255) — so the hardware swap does not change the picture, only the clock.

Cost per clip is about 9 Wh on the 3090, 5.6 Wh on the 4090 — a 60 W bulb for nine and five minutes respectively. Well under a forint of electricity either way.

The second character was easier — and that surprised me

The second test character is Hörcsi, a plush hamster (a real toy, photographed on white). Where the logo needed about a dozen takes to wave properly, Hörcsi waved on the first one — more motion, better stability, no seed hunting.

The reason is worth knowing before you plan your own tests: a photographed plush sits much closer to what these models were trained on than flat vector line-art. So the “hard” character was my own logo, not the toy. The brand risk inverts too — nobody can spot a wrong fur strand, but everybody spots a mascot that has grown a mouth.

Hörcsi, first take, with the male Hungarian voice. Look closely and you can see the weakness: the body jitters slightly while the paw waves.

Prompt, model and settings
  • Video model: Wan 2.2 TI2V-5B (text+image-to-video), fp16 · text encoder umt5-xxl fp8 · Wan 2.2 VAE
  • Hardware: 1× RTX 4090, 24 GB — 53 s per render, ~5.6 Wh
  • Settings: 704×704, 81 frames @ 24 fps, seed 7, 20 steps, euler/simple, cfg 5, shift 8
  • Start frame: the product photo, cut out and pasted onto a larger white canvas at 70 % of frame height, so a raised paw has somewhere to go
  • Voice: Piper, Hungarian male voice, CPU-only, ~4.0 s of audio
  • Assembly: ffmpeg — timing stretched ×1.45 with motion interpolation to 30 fps, audio delayed 0.45 s, loudnorm

Prompt:

A plush toy hamster mascot with soft beige fur, a big cream belly, a small pink heart-shaped nose and shiny black eyes stands centered on a clean white background. He raises one soft rounded plush paw up beside his head and waves it from side to side several times in a big friendly greeting, a clear repeated waving motion. He blinks once, a single quick blink, then keeps his eyes wide open. Photo-realistic plush toy, soft diffuse studio lighting, static camera, constant character size, plain white background.

Negative prompt:

blurry, low quality, watermark, text, letters, extra limbs, extra paws, extra ears, missing ear, deformed, distorted, melting, character morphing, changing face, cartoon drawing, 2D illustration, camera pan, camera zoom, zoom in, scale change, background change, color shift

Note what is not in that negative prompt: no “hands”, no “fingers”. Naming the body part that does the waving is what kills the wave — see below.

A bigger model is not simply a better model

I then ran the largest image-to-video model that fits a 24 GB card — roughly three times the parameters — on the same character, same start frame, same prompt, same seed.

small modellarge model
Render time53 s572 s (10.8×)
Motion2.900.54
Image qualitysofter nose and pawclearly sharper — fur texture, eyelashes, the little heart nose, a real contact shadow

The big model is the better photograph and the worse animation. It stays much closer to the input image, which is exactly what makes its identity hold and its wave nearly vanish. For a mascot whose entire job is to wave, the small model won on the thing that mattered, at a eleventh of the cost.

The same character and the same seed on the large image-to-video model, silent. Sharper fur, real eyelashes, a proper contact shadow — and almost no movement. It also holds rock-steady: none of the jitter in the clip above.

Prompt, model and settings
  • Video model: Wan 2.2 I2V-A14B, fp8 — a two-stage graph: a high-noise pass then a low-noise pass, ~14 GB of weights each, loaded in sequence · Wan 2.1 VAE
  • Hardware: 1× RTX 4090, 24 GB — 572 s per render (10.8× the small model). Peak VRAM ~22.7 GB of 24, so this is the largest Wan that fits a 24 GB card
  • Settings: 704×704, 81 frames @ 24 fps, seed 7, 20 steps split 10/10 between the two stages, euler/simple, cfg 3.5, shift 8
  • Start frame: identical to the clip above
  • Assembly: ffmpeg — same ×1.45 retime and 30 fps interpolation, no audio

Same prompt and negative prompt as the clip above — deliberately, so the only variable is the model.

I would not have guessed that. The lesson I am taking: motion came from the prompt and from retiming in ffmpeg, not from parameter count — so reaching for the flagship model is not the fix when the problem is movement.

The flip side is real too: the small model flickers. Watch the first Hörcsi clip and the body jitters slightly under the waving paw; the large model is completely stable.

The model that gave both — and the catch

There is a third model in the Wan family built for character animation rather than image-to-video. On Hörcsi it did what neither of the others could: the motion of the small model and the sharpness of the large one.

Hörcsi on the character-animation model, with the Hungarian male voice. This is the best on-prem result I got all weekend.

Prompt, model and settings
  • Video model: Wan 2.2 Animate-14B, bf16 — 34.5 GB of weights, which does not fit a 24 GB card. This one ran on the DGX box
  • Hardware: NVIDIA GB10, 130 GB unified memory — 21m59s
  • Settings: 704×704, 81 frames @ 24 fps, seed 7, 20 steps, euler/simple, cfg 5, shift 8 · reference image only (the model also accepts a driving face/pose video, which I did not use)
  • Voice: Piper, Hungarian male voice. Note the name is spelled Hőrcsi with a long ő for the synthesiser only — plain “Hörcsi” comes out wrong. The written name never changes
  • Assembly: ffmpeg — ×1.38 retime with motion interpolation to 30 fps, audio delayed 0.45 s, loudnorm

Same prompt as the Hörcsi clips above.

And then it failed completely on Kolli. I ran the exact same model on the octopus expecting the same win, and got something worse than where I started: both tentacles flailing, the beanie deforming, erraticness of 7.28 against the first version’s 1.28. I threw it away.

The reason makes sense in hindsight. A character-animation model is trained on real characters with bodies, poses and faces. Give it a photographed plush toy and it knows what it is looking at. Give it flat vector line-art and it hallucinates limbs onto it.

So the two mascots need different models, and that is the practical lesson:

CharacterBest modelWhy
Hörcsi — a photographed plushAnimate-14Bclose to what the model was trained on
Kolli — flat vector logoI2V-A14Bstays faithful to the source drawing

“Which model is best” turned out to be the wrong question. Which model suits your character is the right one.

Four findings that cost me the most time

1. A negative prompt that names the body part doing the action kills the action. The waving tentacle kept turning into a hand, so I put hands, fingers, claw in the negative prompt. The hand went away and so did the wave. I made the same mistake twice more — negating “blinking” broke the eyes, negating “out of frame” damped the wave, because a tentacle has to move toward an edge to wave at all. Fix artifacts in the positive prompt; keep the negative for things that are never part of the motion.

2. The model decides how far, ffmpeg decides how fast. Asked for a slower, calmer wave, every word meaning “slow” made the gesture smaller rather than slower — measured, the motion dropped from 1.58 to 0.72 and the tentacle stopped lifting. There is no tempo control. Prompt for the full gesture, then retime it in ffmpeg with motion interpolation.

3. The seed is a stability variable, not a variation knob. Same prompt, same settings — change only the seed and one take holds the character while another gives him a mouth, rotates his body and zooms in. Not subtly worse. Broken.

4. If you cannot hear or see the flaw, measure it. I shipped a clip described as “and now he blinks” that never actually blinks — a partial squint that looked like a blink in the still frames. I had stared at it. So I wrote a twenty-line scorer: blink count from eye-white pixels, motion magnitude, distance from the frame edge. Now I render a batch of seeds, score them, and only open the ones that pass. Same trick on the audio side: generating the Hungarian voice and transcribing it back with a second model caught a mispronunciation of the company name that I could not have heard.

There is also a hard ceiling worth knowing: the small model holds the character for about 3.4 seconds. Past that it rotates, zooms and invents details, and no amount of prompting fixes it — it is the model size, not the words.

How the picture at the top was made

No stock photography and no cloud image API — the same rule as the video. That banner came off one of my own RTX 3090s in 212 seconds.

Prompt, model and settings
  • Model: Qwen-Image 2512, fp8 (Apache-2.0) — the same image stack that shoots the food photography for openchef
  • Hardware: 1× RTX 3090 on the image node — 212 s, 1664×928
  • Served through: a small OpenAI-compatible shim in front of ComfyUI, so a /v1/images/generations call renders it

Prompt:

Two friendly cartoon mascots standing side by side in the centre of a bright white studio: a cheerful round yellow cartoon octopus wearing a royal blue knitted beanie hat, and a chubby beige plush toy hamster with a soft cream belly and a tiny pink heart-shaped nose. A long glossy 35mm film strip curls and spirals through the air around both of them like a flowing ribbon, its little frames catching the light. Behind them stand sleek dark server racks and desktop computer towers with glowing blue and green status lights and visible graphics cards, humming away as they render the film. Clean modern flat vector illustration, bold shapes, bright cheerful colours, soft studio lighting, plain white background, wide banner composition.

Negative prompt:

text, letters, words, watermark, logo, signature, people, human faces, hands, extra limbs, deformed, distorted, blurry, low quality, cluttered, dark, gloomy, photorealistic clutter

One honest note, and it is the article’s own lesson coming back around: the two mascots in that banner are not the real ones. The octopus has a mouth and rosy cheeks that Kolli does not have, and the hamster is not the toy on my desk. A text-to-image model interprets a description; it does not reproduce a brand asset. That is exactly why the videos above start from the actual artwork as a first frame rather than from a prompt — for a banner an interpretation is fine, for a logo it is not.

Where I have got to, and what is next

I am, honestly, quite happy with this. I started the weekend having never generated a frame of video, with a logo that flickered when it waved, and ended it with two mascots that move the way I wanted — everything rendered on hardware sitting in my own building, on open weights, for a few watt-hours a clip. This potentially works. Not “works as a demo” — works well enough that I can see it becoming a real part of how we produce content.

It is not finished, and I would not pretend otherwise:

  • I will compare these against the public models — Google Veo 3.1 (Google AI Plus, 1890 Ft/month), and the talking-avatar services. I have done exactly this for coding, images and speech, and it is the only honest way to know how far behind or ahead the local stack really is. My guess is the cloud wins on raw polish; the interesting question is by how much, and whether that gap matters for a five-second mascot clip.
  • Speech-driven animation is the piece I most want to try: instead of muxing audio under a silent clip, feed the voice to the model and let it drive the character.
  • More characters, longer clips, and the two-minute explainer this was all a rehearsal for.

I will keep testing as time allows and update this post as results come in. If you are doing the same on your own hardware, the four findings above are the ones I would want handed to me on day one.

I will update this post as those land. The short version so far: on-prem video generation works, a single consumer GPU is enough for a mascot clip, and it is noticeably less mature than the text, retrieval, coding and speech parts of my stack — it still needs a batch of seeds and a scorer to find the take that works.


Everything here runs on ATAILA’s own hardware — the same private-AI platform we build for customers who would rather their data and their brand assets stayed in their own building.