DEV Community

Daniel Pertu
Daniel Pertu

Posted on

We deleted our finished videos and shipped the layers instead: rendering transparent overlays with Remotion

Munchable's ad creative used to be seventeen finished vertical videos, rendered end to end with Remotion. Press render, get an MP4, post it.

They are all in git history now. What the package renders today is thirteen transparent clips, a partly transparent end card and 44 transparent PNG icons, and the finished video is assembled in a normal video editor over footage of an actual person talking.

The reason is not technical. A fully rendered product video is a video with nobody in it, and for the kind of short vertical video this app needs, the person on camera is the point. What a camera cannot produce is the phone in the shot showing a real answer. So render exactly that, with nothing behind it, and let the edit do the rest.

The interesting part is everything that changed downstream of that decision.

Alpha has to survive four separate steps

Remotion renders frames and hands them to FFmpeg. Transparency survives only if every step in that chain can carry it, and the defaults cannot:

Config.setVideoImageFormat('png');     // JPEG has no alpha channel at all
Config.setCodec('prores');
Config.setProResProfile('4444');       // the ProRes profile that carries alpha
Config.setPixelFormat('yuva444p10le'); // the 'a' is the whole point
Enter fullscreen mode Exit fullscreen mode

Four lines, and each of them is a different place to silently lose it. Leave the image format at JPEG and the renderer throws away alpha before FFmpeg ever sees it. Pick a lower ProRes profile and you get an opaque file. Leave the pixel format at yuv444p10le, one character off, and you get a file that looks right in a player with a black background and lands in the editor as a black rectangle with your artwork inside it.

Nothing in that chain fails loudly. You find out when you drop the clip onto a timeline and it covers the footage you were decorating.

Putting the settings in remotion.config.ts rather than on the render command matters more than it looks: the Studio's own Render button reads that file. Otherwise the config is correct for the batch render and wrong for every one-off render done while tuning a clip, which is most of them.

Alpha is expensive, and that is fine

ProRes 4444 is roughly 5 MB per second for a caption and 15 MB per second for a phone clip. A ten second phone overlay is around 150 MB.

For a delivery format that would be unacceptable. For an intermediate it is correct: these files exist to be imported once, placed on a track and re-encoded by the editor. Every alternative that compresses better either loses the alpha channel or needs a codec that one of CapCut, Premiere, Final Cut or DaVinci will refuse to import. The whole point is that the person editing does not have to think about it.

The canvas size turned out to be a content decision

Each clip declares its own dimensions, and I expected that to be a formality. It is the thing I changed most.

A full 1080x1920 transparent frame with a phone in the middle of it is technically fine and useless in practice. Dropped onto a timeline it is locked to the full frame: to move the phone left you have to move the whole canvas left, which moves it off screen. So most clips are cropped to their content, centred, with just enough margin for shadows and bounce: a single phone renders at 860x1570, and the sticker, pack, logo and basket likewise. In the editor they behave like stickers, because they are.

Two clips deliberately stay full frame, and the reason is the same in both cases: where they sit is part of what they mean.

  • The caption has to land inside the platforms' safe zone, between roughly 230 px and 1500 px from the top, or the first line ends up under a username and the last under a progress bar.
  • The scan guide draws the app's own corner guide, gold sweep and shutter flash around whatever the person is holding up, and then puts the verdict where the guide was. That is only true if it is positioned against the full frame.

The crop changed the animation too. A cropped clip has no frame edge to slide in from, so nothing enters from off screen. Everything lifts in and fades away in place, which has the side effect of working anywhere in the shot rather than only at the position it was designed for.

And a note that is not a rendering concern at all: when placing a phone, the left side of the shot is the safer one, because the platforms stack their like, comment and share buttons down the right edge. That sentence is in the package README because it is the single most common way one of these overlays gets ruined after it is correctly rendered.

The one clip that keeps a background, sort of

The end card is the exception: cream wipes up from the bottom, the cookie is bitten into the logo, then the tagline. It is the only piece with a background, and even it is transparent above the wipe line. That is what makes it work as an end card rather than a cut: you overlap it with the last moment of your own footage and the cream rises over you.

Everything here is also silent. Thirteen clips, not one audio track. The music bed belongs in the editor where it can be ducked under a voice, and the app itself has no sound effects, so putting a swoosh on the phone overlay would be advertising a product that does not exist.

What stayed the same

Every phone overlay still scores its illustrated pack with the production rules engine at render time, so the phone in the shot cannot show an answer the app would not give. I wrote about why that constraint exists in our ad creative imports the production rules engine, and splitting the reels into overlays did not change it: an overlay is a smaller lie surface than a reel, not a licence for a bigger one.

The app screens these clips draw are the real ones. The recipe list in the phone-recipes overlay is the same set published at munchable.app/recipes, and the condition names on the stickers are the seven in the condition guides. If a clip and the site ever disagree, the clip is wrong, and that is the direction the dependency is pointed on purpose.

Top comments (0)