I'm a developer who also edits YouTube videos, and I kept running into the same silly problem: I needed a 5-second clip, like a film leader countdown, a subscriber counter ticking up, or a search query being typed, and the "easy" route was either a template pack or 20 minutes in After Effects.

So I built EditKitz: a set of tiny, free, single-purpose video tools. You fill in a few settings, watch the live preview, and download an MP4. There's no sign-up and no watermark, and the video never leaves your browser.

This post covers how it works under the hood: rendering real MP4s client-side with Remotion and WebCodecs, and the design rules that keep it simple.

What it does today

Tool What you get
Film Style Countdown Classic 10-to-1 film leader with grain, scratches, flicker, a clock-wipe and optional beeps
Number Counter A number counting up or down to any value on a green screen (prefix, suffix, easing, separators)
Search Bar Typing A query typed letter by letter into a search box, then the button pressed

Every tool exports 16:9, 1:1 or 9:16 at 480p, 720p or 1080p, at 30 or 60 fps.

Film Style Countdown: the number 3 inside two rings on a scratched gray film leader

Number Counter: a white number counting up on a green screen

Search Bar Typing: a query typed into a search box on a green screen

The green-screen ones key out cleanly in any editor, so you can lay them over your own footage.

Why render in the browser?

The usual way to generate video programmatically is a server: headless Chrome plus FFmpeg on a box somewhere. That means hosting costs that grow with every user, upload and download queues, and usually an account system to stop abuse.

For clips that are a few seconds long, the user's machine is more than powerful enough. Browsers can now encode H.264 natively through WebCodecs, so I went browser-first:

  • Zero render servers. The site is a static-ish Next.js app, so hosting costs stay flat however many people use it.
  • Privacy by default. Nothing is uploaded, because there's nothing to upload to.
  • No accounts. With no server-side cost, there's nothing to gate.

The trade-offs: only part of CSS is supported, renders run on a single thread, and it only works where WebCodecs does. I check that with feature detection, never a browser-name check.

The stack: Remotion's web renderer

Remotion lets you write video as React components: each frame is a pure function of useCurrentFrame(). Its newer @remotion/web-renderer package renders those components to an MP4 directly in the browser.

The whole export path is one small, tool-agnostic wrapper. Each tool passes in its composition and props, and gets back a Blob:

export const renderToBlob = async (spec, { onProgress, signal } = {}) => { // Loaded lazily on first export, so the page bundle stays light const { renderMediaOnWeb } = await import("@remotion/web-renderer"); const { getBlob } = await renderMediaOnWeb({ composition: { component: spec.component, id: spec.id, width: spec.width, height: spec.height, fps: spec.fps ?? 30, durationInFrames: spec.durationInFrames, }, inputProps: spec.inputProps, container: "mp4", videoCodec: "h264", muted: !spec.withAudio, signal, // cancel button support onProgress: (p) => onProgress?.(p.progress), }); return getBlob(); }; 

Before showing the Export button, I ask the renderer whether this browser can actually do it:

const { canRenderMediaOnWeb } = await import("@remotion/web-renderer"); const result = await canRenderMediaOnWeb({ container: "mp4", videoCodec: "h264", width, height, muted: !withAudio, }); 

One detail I like: if the check fails only because the browser can't encode audio, I don't block the export. I run the check again muted and export a silent video with a small notice. A missing beep shouldn't stop someone from getting their countdown.

Rule #1: every frame is a pure function

The most useful design rule is that the same input always produces the same video. There's no Math.random() and no wall-clock time, and no AI models are involved. Everything derives from the frame number.

Take the Search Bar Typing tool. Every letter takes the same time, so how many characters are visible is plain arithmetic:

// frame = frames since typing started const visibleChars = (frame: number, charCount: number, charsPerSecond: number, fps: number) => Math.min(charCount, Math.max(0, Math.floor(((frame + 1) * charsPerSecond) / fps))); 

That one rule pays off a lot:

  • Preview = export. The <Player> preview and the encoded MP4 run the same function, so what you see is what you get.
  • Scrubbing just works. Jump to any frame and it's correct, with no state to replay.
  • 60 fps for free. The timeline is authored at 30 fps. A 60 fps export samples the same timeline at fractional base frames (frame * 30 / 60), so motion is smoother but everything still happens at the same moment.
  • Easy unit tests. "Frame 0 shows no letters, the last typing frame shows the whole query" is a two-line test.

The film countdown has grain, scratches and flicker, and even those are seeded from the frame number rather than random, so two exports of the same settings are identical.

Rule #2: one tool, one job

Each tool is a self-contained feature folder with its own composition, editor and limits. A single registry lists them all, and the home page, routes and sitemap are generated from it, so shipping a new tool means adding one line:

const tools: ToolDefinition[] = [filmStyleCountdownTool, numberCounterTool, searchBarTypingTool]; 

Keeping each tool narrow (one search box, one query; one number per video) is what makes them fast to use. When it's tempting to add typos, a suggestions dropdown or camera moves, those go into a "later" issue instead of growing the v1.

Rule #3: compositions stay portable

Compositions are plain React with SVG and plain props, with no browser-only APIs inside them. That keeps the door open to rendering the same components server-side with @remotion/renderer later (batch exports, longer videos) without rewriting a single tool.

What I learned

  • WebCodecs is ready for real work. H.264 encoding in the browser is fast enough for short clips at 1080p.
  • Lazy-load the renderer. Most visitors play with the preview before exporting, so the encoder is only fetched on the first export.
  • Feature-detect and degrade gracefully. Fall back to a silent export rather than an error.
  • Determinism is a superpower for video tools. It makes testing, previewing and debugging boring, in a good way.

Try it, and tell me what to build next

👉 editkitz.com: free, no sign-up, no watermark.

More tools are on the way (progress bars, more countdown styles and more). What small clip do you keep making by hand in your editor? Drop it in the comments, and the most requested one gets built next.