<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ashutosh Sharma</title>
    <description>The latest articles on DEV Community by Ashutosh Sharma (@ashutosh_sharma_899695c36).</description>
    <link>https://dev.to/ashutosh_sharma_899695c36</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4242771%2Fa50ce65d-4483-4b58-8710-894ff79c411d.png</url>
      <title>DEV Community: Ashutosh Sharma</title>
      <link>https://dev.to/ashutosh_sharma_899695c36</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ashutosh_sharma_899695c36"/>
    <language>en</language>
    <item>
      <title>EditKitz (editkitz.com): free browser video tools, now with a Bar Chart Race maker</title>
      <dc:creator>Ashutosh Sharma</dc:creator>
      <pubDate>Sat, 14 Nov 2026 07:49:24 +0000</pubDate>
      <link>https://dev.to/ashutosh_sharma_899695c36/editkitz-editkitzcom-free-browser-video-tools-now-with-a-bar-chart-race-maker-ida</link>
      <guid>https://dev.to/ashutosh_sharma_899695c36/editkitz-editkitzcom-free-browser-video-tools-now-with-a-bar-chart-race-maker-ida</guid>
      <description>&lt;p&gt;I'm building &lt;a href="https://www.editkitz.com/?utm_source=devto" rel="noopener noreferrer"&gt;EditKitz&lt;/a&gt;, a small kit of free browser tools for making short videos. Each tool does one job: set it up, preview it live, export an MP4. The newest one is &lt;strong&gt;Bar Chart Race&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpytbcgt2g8z80mubcf9w.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpytbcgt2g8z80mubcf9w.webp" alt="Bar Chart Race preview" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why
&lt;/h2&gt;

&lt;p&gt;Every bar chart race tool I found wanted an account, a subscription, or put a watermark on the export. I wanted something I could open, paste data into, and get a clean MP4 from.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Paste CSV, or copy straight from Excel or Google Sheets (one row per item, one column per step)&lt;/li&gt;
&lt;li&gt;Live preview, then export MP4 at 480p, 720p or 1080p, 30 or 60 fps&lt;/li&gt;
&lt;li&gt;16:9, 1:1 or 9:16, for YouTube, feeds and Shorts or Reels&lt;/li&gt;
&lt;li&gt;4 palettes, a color per item, 6 fonts, title and step label options, any background (green screen included)&lt;/li&gt;
&lt;li&gt;Up to 30 items and 40 steps, clips from 5 to 120 seconds&lt;/li&gt;
&lt;li&gt;Items slide in or out when their cells are empty, and gaps in the middle are filled in&lt;/li&gt;
&lt;li&gt;Errors name the exact cell, like "Row 3, 2021: '12a' is not a number."&lt;/li&gt;
&lt;li&gt;Your data stays in your browser. No uploads, no account, no watermark.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.editkitz.com/tools/bar-race?utm_source=devto" rel="noopener noreferrer"&gt;https://www.editkitz.com/tools/bar-race?utm_source=devto&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The sample data is illustrative, not real market share. It's v1, so logos on bars, sound and sorting options aren't there yet. Tell me what you'd want next, or which tool I should build after this one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Also in the kit
&lt;/h2&gt;

&lt;p&gt;All free, no sign-up, MP4 export:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.editkitz.com/tools/number-counter?utm_source=devto" rel="noopener noreferrer"&gt;Number Counter&lt;/a&gt;: animated counting numbers&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.editkitz.com/tools/film-style-countdown?utm_source=devto" rel="noopener noreferrer"&gt;Film-Style Countdown&lt;/a&gt;: classic film leader countdowns&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.editkitz.com/tools/search-bar-typing?utm_source=devto" rel="noopener noreferrer"&gt;Search Bar Typing&lt;/a&gt;: a search box typing out your query&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More tools are on the way.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>dataviz</category>
      <category>video</category>
      <category>tooling</category>
    </item>
    <item>
      <title>I built free video tools that render MP4s entirely in the browser (Remotion + WebCodecs)</title>
      <dc:creator>Ashutosh Sharma</dc:creator>
      <pubDate>Fri, 13 Nov 2026 11:29:36 +0000</pubDate>
      <link>https://dev.to/ashutosh_sharma_899695c36/i-built-free-video-tools-that-render-mp4s-entirely-in-the-browser-remotion-webcodecs-3b8l</link>
      <guid>https://dev.to/ashutosh_sharma_899695c36/i-built-free-video-tools-that-render-mp4s-entirely-in-the-browser-remotion-webcodecs-3b8l</guid>
      <description>&lt;p&gt;I'm a developer who also edits YouTube videos, and I kept running into the same silly problem: I needed a &lt;strong&gt;5-second clip&lt;/strong&gt;, 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.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;&lt;a href="https://www.editkitz.com/?utm_source=devto" rel="noopener noreferrer"&gt;EditKitz&lt;/a&gt;&lt;/strong&gt;: 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 &lt;strong&gt;never leaves your browser&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does today
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What you get&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.editkitz.com/tools/film-style-countdown?utm_source=devto" rel="noopener noreferrer"&gt;Film Style Countdown&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Classic 10-to-1 film leader with grain, scratches, flicker, a clock-wipe and optional beeps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.editkitz.com/tools/number-counter?utm_source=devto" rel="noopener noreferrer"&gt;Number Counter&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A number counting up or down to any value on a green screen (prefix, suffix, easing, separators)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.editkitz.com/tools/search-bar-typing?utm_source=devto" rel="noopener noreferrer"&gt;Search Bar Typing&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A query typed letter by letter into a search box, then the button pressed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every tool exports 16:9, 1:1 or 9:16 at 480p, 720p or 1080p, at 30 or 60 fps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7nl1j21iwwf3b1bx2oro.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7nl1j21iwwf3b1bx2oro.png" alt="Film Style Countdown: the number 3 inside two rings on a scratched gray film leader" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcw6o7pv2zdp1gy4hb1iv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcw6o7pv2zdp1gy4hb1iv.png" alt="Number Counter: a white number counting up on a green screen" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnqvwr231pbjrn5z96h16.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnqvwr231pbjrn5z96h16.png" alt="Search Bar Typing: a query typed into a search box on a green screen" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The green-screen ones key out cleanly in any editor, so you can lay them over your own footage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why render in the browser?
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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 &lt;strong&gt;WebCodecs&lt;/strong&gt;, so I went browser-first:&lt;/p&gt;

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

&lt;p&gt;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 &lt;strong&gt;feature detection, never a browser-name check&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stack: Remotion's web renderer
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.remotion.dev" rel="noopener noreferrer"&gt;Remotion&lt;/a&gt; lets you write video as React components: each frame is a pure function of &lt;code&gt;useCurrentFrame()&lt;/code&gt;. Its newer &lt;code&gt;@remotion/web-renderer&lt;/code&gt; package renders those components to an MP4 directly in the browser.&lt;/p&gt;

&lt;p&gt;The whole export path is one small, tool-agnostic wrapper. Each tool passes in its composition and props, and gets back a &lt;code&gt;Blob&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;renderToBlob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;onProgress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Loaded lazily on first export, so the page bundle stays light&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;renderMediaOnWeb&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"@remotion/web-renderer"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;getBlob&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;renderMediaOnWeb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;composition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;fps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fps&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;durationInFrames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;durationInFrames&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;inputProps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inputProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"mp4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;videoCodec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"h264"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;muted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;withAudio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// cancel button support&lt;/span&gt;
    &lt;span class="n"&gt;onProgress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;onProgress&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;getBlob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before showing the Export button, I ask the renderer whether this browser can actually do it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;canRenderMediaOnWeb&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"@remotion/web-renderer"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;canRenderMediaOnWeb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"mp4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;videoCodec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"h264"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;muted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;withAudio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One detail I like: if the check fails &lt;strong&gt;only&lt;/strong&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule #1: every frame is a pure function
&lt;/h2&gt;

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

&lt;p&gt;Take the Search Bar Typing tool. Every letter takes the same time, so how many characters are visible is plain arithmetic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// frame = frames since typing started&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;visibleChars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;charCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;charsPerSecond&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;charCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;charsPerSecond&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;fps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That one rule pays off a lot:&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule #2: one tool, one job
&lt;/h2&gt;

&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ToolDefinition&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;filmStyleCountdownTool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;numberCounterTool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;searchBarTypingTool&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule #3: compositions stay portable
&lt;/h2&gt;

&lt;p&gt;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 &lt;code&gt;@remotion/renderer&lt;/code&gt; later (batch exports, longer videos) without rewriting a single tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

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

&lt;h2&gt;
  
  
  Try it, and tell me what to build next
&lt;/h2&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://www.editkitz.com/?utm_source=devto" rel="noopener noreferrer"&gt;editkitz.com&lt;/a&gt;&lt;/strong&gt;: free, no sign-up, no watermark.&lt;/p&gt;

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

</description>
      <category>webdev</category>
      <category>react</category>
      <category>showdev</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
