Our shiny videos are React components

Sam·Founding EngineerOct 2

We just launched Android support, and we wanted a short video to go with it: something for Slack, LinkedIn and our website, and something that would look smart and on-brand. However, nobody on the team is a motion designer, and we didn't want a one-off video file that nobody could change later.

If this sounds like another nut to smash with the Claude hammer, you'd be absolutely right. I took the same instinct as our docs screenshots: if it's derived from code, anyone can regenerate it — and now everyone in the team has the ability to generate a launch video.

But first, here's what we ended up with:

Android launch video

A video is just a function

This might be the most engineer-brained thing I've said all week, but a video is really just a function of the frame number.

Remotion takes this approach by rendering React components to an MP4: every frame is a render with a different useCurrentFrame(). Animation is a mapping from frame number to value, so a headline sliding into place is just a number going from 240 to 0 over fourteen frames, with some fancy maths to give it a cute little bounce at the end. A 22-second video winds up being a few hundred lines of TSX.

Each scene of the video is its own component which can be previewed in Remotion Studio, and you stitch them together with a TransitionSeries. Although you can edit things by hand in Remotion Studio, I've found it easier and more productive to just let Claude write the TSX, while I watch some previews.

The project sits in our repo, alongside all of our other apps. As part of this, we decided to hoist all of our brand assets up into a @semaloop/brand package. This means that everything remains consistent, and we don't end up with subtle differences between logos, and .ttf files littered everywhere.

Now, we run npm run render android-launch, and it gives us a nice shiny MP4.

Built with Claude

Remotion publishes agent skills for Claude Code, so the first prompt was essentially "make a video celebrating Android support, look at our website for fonts and colours". The useful loop here was that Claude could render stills from each scene, look at them, and fix what's wrong before I needed to see it.

A genuine highlight of this? "The shed looks like a house, because the roof is too tall" is a real sentence from that session. Saved me a turn (and from having to look at a janky shed). One nice thing here is that I also instructed Claude to check claims in the video against our codebase and documentation before they go in a video. The first draft showed one test running on an iPhone and an Android phone side by side, which we don't support (yet), so that scene was left on the cutting room floor.

Remaining on brand

After the first video, the second one started copying bits out of the first. We made a kit in src/brand/: a Scene with five themes, Headline, Agent, line patterns, timeline cards, a phone schematic, all sorts.

This was all distilled from a couple of "branding" Figma files. Rather than working from screenshots, we signed into Figma in a Playwright browser and read the layers directly through Figma's Plugin API. This allowed Claude to pull out exact colours, type styles, cursor sizes and stroke widths. The composition rules came along too: left-align on a 160px margin, lots of empty space, and one accent per scene. There's a showcase video that uses every piece of this, so you pick a style by watching it rather than reading code.

All these rules live in the app's CLAUDE.md, so the next video starts from the same style regardless of whether it's written by a human or an agent. Our second video was a light-hearted one for the Product Engineer role, with Charlie's shed as our first device farm (no animals).

Thumbnails

This was the longest fight. We wanted to control the thumbnail when the video is posted in Slack, or on LinkedIn. MP4 doesn't have a "poster" setting. Being ffmpeg wizards, we rolled up our sleeves and cracked out the -disposition:v:1 attached_pic. No luck though: Finder on macOS likes this, but things like Slack sadly don't. WebM's cover image seems to get dropped when Slack converts it. GIFs don't help either, and the file would be huge regardless.

Most people seem to just make frame 0 the poster frame, I'm adamant that there's another way that we've just not discovered yet...

Roll credits

Anyone in the team can now produce an on-brand video, and they're also something we can change: fix a typo, re-render, done. Coding agents speed us up along the way, because at the end of the day, it's just React. Those agents stick to guidelines we've given, and the end result is slick.

If you want to find out more about product behind the shiny videos, book a demo at semaloop.com.


Sam·Founding Engineer·Oct 2