<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>webm on S Anand</title>
    <link>https://www.s-anand.net/blog/tag/webm/</link>
    <description>Recent content in webm on S Anand</description>
    <generator>Hugo -- 0.156.0</generator>
    <language>en-us</language>
    <lastBuildDate>Sun, 14 Jun 2026 08:27:16 +0530</lastBuildDate>
    <atom:link href="https://www.s-anand.net/blog/tag/webm/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Editing Workshop Videos</title>
      <link>https://www.s-anand.net/blog/editing-workshop-videos/</link>
      <pubDate>Sun, 14 Jun 2026 08:27:16 +0530</pubDate>
      <guid>https://www.s-anand.net/blog/editing-workshop-videos/</guid>
      <description>&lt;p&gt;I sometimes use &lt;a href=&#34;https://meet.google.com/&#34;&gt;Google Meet&lt;/a&gt;, &lt;a href=&#34;https://teams.microsoft.com/&#34;&gt;Teams&lt;/a&gt;, &lt;a href=&#34;https://www.zoom.com/&#34;&gt;Zoom&lt;/a&gt;, etc. to record workshops and talks. These record the entire session, including &lt;em&gt;before&lt;/em&gt; and &lt;em&gt;after&lt;/em&gt; the actual talk, and save it as large MP4 files.&lt;/p&gt;
&lt;p&gt;I use &lt;code&gt;ffmpeg&lt;/code&gt; to trim the video to just the talk, and then compress it for sharing. I&amp;rsquo;m sharing the options that work for me, discovered by trial-and-error.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;To trim it&lt;/strong&gt;, I use the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ffmpeg -ss 00:10:00 -to 02:10:00 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -i &lt;span class=&#34;s2&#34;&gt;&amp;#34;original.mp4&amp;#34;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -map &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -c copy &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -avoid_negative_ts make_zero &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -movflags +faststart &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  new.mp4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Arguments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-ss 00:10:00&lt;/code&gt;: Start reading from 10:00 into the source file. Placing -ss before -i makes this fast because ffmpeg seeks instead of decoding from the start.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-to 02:10:00&lt;/code&gt;: Stop at 02:10:00 in the source timeline. So this keeps roughly the section from 00:10:00 to 02:10:00.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-map 0&lt;/code&gt;: Keep all streams from the input: video, audio, subtitles, metadata streams, etc. Use &lt;code&gt;-map 0:v:0 -map 0:a:0&lt;/code&gt; for just the first video and audio streams.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-c copy&lt;/code&gt;: Do not re-encode. Just copy the existing encoded streams. This is very fast and preserves original quality, but cuts only cleanly near keyframes.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-avoid_negative_ts make_zero&lt;/code&gt;: Rewrite timestamps so the output starts cleanly near zero. This helps avoid weird negative timestamps and duration/reporting issues in some players.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-movflags +faststart&lt;/code&gt;: Move MP4 metadata to the beginning of the file. This helps playback start faster in browsers and streaming contexts.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;To compress it&lt;/strong&gt; I use:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ffmpeg -i &lt;span class=&#34;s1&#34;&gt;&amp;#39;trimmed.mp4&amp;#39;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -map 0:v:0 -map 0:a:0 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -c:v libvpx-vp9 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -b:v &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -crf &lt;span class=&#34;m&#34;&gt;42&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -deadline realtime &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -cpu-used &lt;span class=&#34;m&#34;&gt;8&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -row-mt &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -tile-columns &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -frame-parallel &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -threads &lt;span class=&#34;m&#34;&gt;8&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -pix_fmt yuv420p &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -vf &lt;span class=&#34;s2&#34;&gt;&amp;#34;fps=16&amp;#34;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -c:a libopus &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -b:a 32k &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -ac &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -ar &lt;span class=&#34;m&#34;&gt;48000&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -af &lt;span class=&#34;s2&#34;&gt;&amp;#34;highpass=f=80,lowpass=f=12000&amp;#34;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;compressed.webm&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Arguments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-map 0:v:0 -map 0:a:0&lt;/code&gt;: Use the first video stream and first audio stream from the input&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-c:v libvpx-vp9&lt;/code&gt;: Compress video with VP9 codec&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-b:v 0&lt;/code&gt;: Use constant-quality mode instead of targeting a fixed bitrate&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-crf 42&lt;/code&gt;: When testing on samples, CRF 42 was small enough and clear enough for me&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-deadline realtime&lt;/code&gt;: &lt;code&gt;-deadline good&lt;/code&gt; is too slow for me though that&amp;rsquo;s better for production&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-cpu-used 8&lt;/code&gt;: 8 is the maximum allowed for VP9 for now&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-row-mt 1&lt;/code&gt;: Enable row-based multithreading for faster VP9 encoding&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-tile-columns 2&lt;/code&gt;: Split each frame into tiles so multiple threads can encode in parallel&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-frame-parallel 1&lt;/code&gt;: Allow frames to be decoded in parallel; useful for VP9/WebM playback&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-threads 8&lt;/code&gt;: Use up to 8 CPU threads during encoding&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-pix_fmt yuv420p&lt;/code&gt;: Use the most compatible pixel format for browsers and video players&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-vf &amp;quot;fps=16&amp;quot;&lt;/code&gt;: Reduce frame rate to 16 fps to shrink file size; good enough for slides/talk videos&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-c:a libopus&lt;/code&gt;: Compress audio with Opus, the standard audio codec for WebM&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-b:a 32k&lt;/code&gt;: Use low audio bitrate; enough for speech&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-ac 1&lt;/code&gt;: Convert audio to mono; good for voice and halves stereo audio data&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-ar 48000&lt;/code&gt;: Use 48 kHz, the standard sample rate for Opus&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-af &amp;quot;highpass=f=80,lowpass=f=12000&amp;quot;&lt;/code&gt;: Remove very low rumble and very high noise; keeps speech frequencies&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I deploy these on CloudFlare R2 on a custom domain: &lt;a href=&#34;https://media.s-anand.net/&#34;&gt;media.s-anand.net&lt;/a&gt;. &lt;a href=&#34;https://developers.cloudflare.com/r2/pricing/&#34;&gt;R2 costs&lt;/a&gt; 1.5c / GB-month. Storing 10 years&amp;rsquo; worth of ~500 talks of ~0.5 GB each is under $4 / month - quite affordable. (If it increases, I can shift. Statis files are easy to move.)&lt;/p&gt;
&lt;p&gt;I serve them via a &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag. Here is an &lt;a href=&#34;https://media.s-anand.net/2026-05-23-ai-unboxed-context-engineering.webm&#34;&gt;example video&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;video&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;preload&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;metadata&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;width&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;1920&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;height&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;1080&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;controls&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;style&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;max-width: 100%; height: auto;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;source&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;https://media.s-anand.net/2026-05-23-ai-unboxed-context-engineering.webm&amp;#34;&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;video/webm; codecs=&amp;amp;quot;vp9, opus&amp;amp;quot;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;video&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;video preload=&#34;metadata&#34; width=&#34;1920&#34; height=&#34;1080&#34; controls style=&#34;max-width: 100%; height: auto;&#34;&gt;
  &lt;source src=&#34;https://media.s-anand.net/2026-05-23-ai-unboxed-context-engineering.webm&#34; type=&#34;video/webm; codecs=&amp;quot;vp9, opus&amp;quot;&#34;&gt;
&lt;/video&gt;
</description>
    </item>
    <item>
      <title>Recording screencasts</title>
      <link>https://www.s-anand.net/blog/recording-screencasts/</link>
      <pubDate>Wed, 11 Mar 2026 10:48:01 +0800</pubDate>
      <guid>https://www.s-anand.net/blog/recording-screencasts/</guid>
      <description>&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;https://files.s-anand.net/images/2026-03-11-recording-screencasts.avif&#34;&gt; &lt;!-- https://gemini.google.com/u/2/app/6cf35e0e4b2a90da --&gt;&lt;/p&gt;
&lt;p&gt;Since &lt;a href=&#34;https://www.s-anand.net/blog/ai-video-compression/&#34;&gt;WEBM compresses videos &lt;strong&gt;very&lt;/strong&gt; efficiently&lt;/a&gt;, I&amp;rsquo;ve started using videos more often. For example, in &lt;a href=&#34;https://www.s-anand.net/blog/prototyping-the-prototypes/&#34;&gt;Prototyping the prototypes&lt;/a&gt; and in &lt;a href=&#34;https://www.s-anand.net/blog/using-game-playing-agents-to-teach/&#34;&gt;Using game-playing agents to teach&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I use a &lt;a href=&#34;https://github.com/sanand0/scripts/blob/e0049d7b79d142e23b64fe9ce2c2a383ca8250a6/setup.fish#L442-L475&#34;&gt;fish script to compress screencasts&lt;/a&gt; like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Increase quality with lower crf= (55 is default, 45 is better/larger)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# and higher fps= (5 is default, 10 is better/larger).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;screencastcompress --crf &lt;span class=&#34;m&#34;&gt;45&lt;/span&gt; --fps &lt;span class=&#34;m&#34;&gt;10&lt;/span&gt; a.webm b.webm ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To &lt;em&gt;record&lt;/em&gt; the screencasts, I prefer slightly automated approaches for ease and quality.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;METHOD 1: Browser scrolling&lt;/strong&gt;: To uniformly scroll to the bottom of the page at ~800 pixels / second (roughly one page per second), I frame the recording window, start &lt;a href=&#34;https://help.gnome.org/gnome-help/screen-shot-record.html&#34;&gt;recording&lt;/a&gt;, and &lt;em&gt;quickly&lt;/em&gt; paste this script in the DevTools console:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;scrollInterval&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;setInterval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nb&#34;&gt;window&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;scrollBy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// Scroll 16px every 20ms (approx 800px/s)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;window&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;innerHeight&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;window&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;scrollY&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;document&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;scrollHeight&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;clearInterval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;scrollInterval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Reached bottom or no more scrolling.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is what I used in &lt;a href=&#34;https://www.s-anand.net/blog/prototyping-the-prototypes/&#34;&gt;Prototyping the prototypes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;METHOD 2: AI Coding Agents&lt;/strong&gt;: I tell the coding agent to:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;hellip; screenshot each step at 1366x768, compressing into video.webm at 1.5 fps, pausing the last frame for 5 seconds.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If playwright and ffmpeg are set up, this &lt;em&gt;just works&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This is what I used in &lt;a href=&#34;https://www.s-anand.net/blog/using-game-playing-agents-to-teach/&#34;&gt;Using game-playing agents to teach&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>AI video compression</title>
      <link>https://www.s-anand.net/blog/ai-video-compression/</link>
      <pubDate>Sat, 28 Feb 2026 09:18:56 +0800</pubDate>
      <guid>https://www.s-anand.net/blog/ai-video-compression/</guid>
      <description>&lt;p&gt;I recorded a short screen cast of a demo I built. It was ~900KB - way too large to publish as a thumbnail. So I asked ChatGPT:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What&amp;rsquo;s the best equivalent of squoosh.app for WEBM compression? I&amp;rsquo;m looking for a free modern high-quality online video compressor.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There are a few, and they compressed it to a third of its size, but 300KB is still too large. So I attached the original and asked:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I am compressing screenshots like this. They&amp;rsquo;re often not large. I don&amp;rsquo;t mind cropping the edges by a few pixels to make it a multiple 2, 4, or 8 if that&amp;rsquo;ll help. I certainly am OK with a lower frame rate. I&amp;rsquo;d like an image quality that a human eye can just SLIGHTLY detect as worse than the original, but only VERY SLIGHTLY.&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s the best way of using ffmpeg or similar tools to compress such a file?&lt;/p&gt;
&lt;p&gt;Think like an expert:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What would an expert in this field check that beginners would miss?&lt;/li&gt;
&lt;li&gt;What patterns would an expert in this field recognize that beginners would miss?&lt;/li&gt;
&lt;li&gt;In this context, what questions would an expert ask that a beginner would not know to?&lt;/li&gt;
&lt;li&gt;If this goes wrong, what are the most likely reasons?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;hellip; and list the best (diverse, testing different hypotheses) compression command options.&lt;/p&gt;
&lt;p&gt;Run them on this video and let me download the resulting videos for visual comparison. Interview me. Give me a list of questions that I can easily answer by looking at the videos and I&amp;rsquo;ll share those with you to help you decide the best compression command for me.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This leverages 3 tricks.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;These are online coding agents. So they can write &lt;em&gt;and&lt;/em&gt; run code.&lt;/li&gt;
&lt;li&gt;The &amp;ldquo;Think like an expert&amp;rdquo; prompt is my new &amp;ldquo;Think step by step&amp;rdquo; prompt and works quite well.&lt;/li&gt;
&lt;li&gt;The &amp;ldquo;Interview me&amp;rdquo; prompt is another powerful one that helps me apply preferences &amp;ndash; and &lt;a href=&#34;https://www.s-anand.net/blog/how-to-develop-taste/&#34;&gt;develop taste&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a href=&#34;https://claude.ai/share/73d1caae-4e4e-4c1b-8984-44ac3e86f7cf&#34;&gt;Claude&lt;/a&gt; did a good job of showing different versions and compressing it, but as expected, &lt;a href=&#34;https://chatgpt.com/share/69a24385-87b8-8003-8f41-cb3757a62d13&#34;&gt;ChatGPT&lt;/a&gt; was the obsessive perfectionist. It gave me a huge set of variations of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Files sizes&lt;/li&gt;
&lt;li&gt;Compression formats (VP9: more compatible vs AV1: better compression)&lt;/li&gt;
&lt;li&gt;Quality settings (CRF)&lt;/li&gt;
&lt;li&gt;Frame rates (FPS)&lt;/li&gt;
&lt;li&gt;Color formats (YUV420p vs YUV444p)&lt;/li&gt;
&lt;li&gt;Compression effort (presets)&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;hellip; and asked me to compare between them, like these:&lt;/p&gt;
&lt;div style=&#34;display:flex; gap:12px; flex-wrap:nowrap; align-items:flex-start;&#34;&gt;
  &lt;figure style=&#34;margin:0; text-align:center;&#34;&gt;
    &lt;video src=&#34;https://files.s-anand.net/images/2026-02-28-sql-screencast-crf45-fps15.webm&#34; autoplay loop muted playsinline preload=&#34;metadata&#34; width=&#34;230&#34;&gt;&lt;/video&gt;
    &lt;figcaption&gt;crf: 45 fps: 15 (93K)&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figure style=&#34;margin:0; text-align:center;&#34;&gt;
    &lt;video src=&#34;https://files.s-anand.net/images/2026-02-28-sql-screencast-crf50-fps15.webm&#34; autoplay loop muted playsinline preload=&#34;metadata&#34; width=&#34;230&#34;&gt;&lt;/video&gt;
    &lt;figcaption&gt;crf: 50 fps: 15 (69K)&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figure style=&#34;margin:0; text-align:center;&#34;&gt;
    &lt;video src=&#34;https://files.s-anand.net/images/2026-02-28-sql-screencast-crf55-fps5.webm&#34; autoplay loop muted playsinline preload=&#34;metadata&#34; width=&#34;230&#34;&gt;&lt;/video&gt;
    &lt;figcaption&gt;crf: 55 fps: 5 (23K)&lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;The final result is this script:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ffmpeg -hide_banner -stats -v warning -i &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$input&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -vf &lt;span class=&#34;s2&#34;&gt;&amp;#34;crop=iw-mod(iw\,2):ih-mod(ih\,2),fps=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$fps&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -c:v libsvtav1 -preset &lt;span class=&#34;m&#34;&gt;8&lt;/span&gt; -crf &lt;span class=&#34;nv&#34;&gt;$crf&lt;/span&gt; -pix_fmt yuv420p &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -an &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$output&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;hellip; which does the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Crops the video to a multiple of 2 (which is required for some compression formats)&lt;/li&gt;
&lt;li&gt;Sets the frame rate to a lower value (which reduces file size)&lt;/li&gt;
&lt;li&gt;Uses the AV1 codec (which has better compression than VP9)&lt;/li&gt;
&lt;li&gt;Sets the CRF (Constant Rate Factor) to a value that balances quality and file size&lt;/li&gt;
&lt;li&gt;Sets the pixel format to YUV420p (which is more compatible with players)&lt;/li&gt;
&lt;li&gt;Disables audio (which is not needed for a screencast)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I realized that for the resolution I&amp;rsquo;ll likely see this at, very low frame rates (5 fps) and poor compressions (CRF 55) are good enough.&lt;/p&gt;
&lt;p&gt;My original video was 912KB. The smallest video that looks good enough for me is 23 KB. That&amp;rsquo;s almost a &lt;strong&gt;40x compression&lt;/strong&gt; - small enough to &lt;a href=&#34;https://sanand0.github.io/datastories/sql-migration/&#34;&gt;publish in my data story&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Things I&amp;rsquo;m taking away:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use AI to discover the best configurations for your tool&lt;/li&gt;
&lt;li&gt;Interview yourself to apply preferences and develop taste&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    <item>
      <title>Things I Learned - 22 Feb 2026</title>
      <link>https://www.s-anand.net/blog/things-i-learned-22-feb-2026/</link>
      <pubDate>Sun, 22 Feb 2026 00:00:00 +0000</pubDate>
      <guid>https://www.s-anand.net/blog/things-i-learned-22-feb-2026/</guid>
      <description>&lt;p&gt;This week, I learned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/tree-sitter/tree-sitter&#34;&gt;tree-sitter&lt;/a&gt; is a fast incremental parser generator. That means you can use it to create a parser for &lt;em&gt;any&lt;/em&gt; language that works even if there are errors, e.g. malformed JSON, Python, etc. It&amp;rsquo;s used by most editors. For example, &lt;a href=&#34;https://github.com/tree-sitter/tree-sitter-python&#34;&gt;tree-sitter-python&lt;/a&gt; is a fast forgiving Python parser. There are &lt;a href=&#34;https://github.com/tree-sitter/tree-sitter/wiki/List-of-parsers&#34;&gt;official parsers&lt;/a&gt; and &lt;a href=&#34;https://github.com/tree-sitter-grammars&#34;&gt;community parsers&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Programming Languages:&lt;/strong&gt; All popular ones, less popular ones like Ada, Fortran, Lua, Zig, &amp;hellip; and even niche / domain-specific languages (Gleam, TLA⁺).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Markup &amp;amp; Data Formats:&lt;/strong&gt; HTML, XML, Markdown, JSON, YAML, TOML, CSV, &amp;hellip;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Query, Scripting &amp;amp; Config:&lt;/strong&gt; SQL, GraphQL, Bash, Dockerfile, Regex, Terraform (HCL), &amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Ligature fonts are nice, but it might not be worth forming a habit out of. &lt;a href=&#34;https://claude.ai/share/3cb9d834-b85a-4aa6-bf9f-e7051101d5c6&#34;&gt;Claude&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Cloudflare introduced &lt;a href=&#34;https://blog.cloudflare.com/markdown-for-agents/&#34;&gt;Markdown for Agents&lt;/a&gt;. This converts websites from HTML to Markdown via &lt;code&gt;Accept: text/markdown&lt;/code&gt; for any Cloudflare endpoint which has enabled this feature. This requires a Pro account.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/nayafia/microgrants&#34;&gt;Microgrants&lt;/a&gt; is a list of microgrants programs - where you can give small amounts of money, e.g. $50 - $1K as well as large fellowships over $100K. This includes student grants, creative &amp;amp; community grants, tech grants, social &amp;amp; policy grants, etc.&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Animated web formats are simply video codecs &amp;hellip; stripped of their most powerful feature.&amp;rdquo; A &lt;code&gt;.webm&lt;/code&gt; file is likely to compress &lt;em&gt;much&lt;/em&gt; better than an animated &lt;code&gt;.webp&lt;/code&gt;, etc. &lt;a href=&#34;https://gemini.google.com/share/a83f37623107&#34;&gt;Gemini&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;esbuild can compile CSS files to support old browsers, e.g. nested rules, custom properties, etc. Usage: &lt;code&gt;esbuild input.css --target=chrome90 --outfile=output.css&lt;/code&gt;. &lt;a href=&#34;https://jvns.ca/til/esbuild-can-build-css/&#34;&gt;Julia Evans&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;New jargon I learnt: &lt;a href=&#34;https://tools.s-anand.net/askai/?q=What+is+Human+On+The+Loop+vs+Human+In+The+Loop%3F+Give+it+to+me+in+a+sentence.&#34;&gt;Human-On-The-Loop&lt;/a&gt;. &lt;a href=&#34;https://tools.s-anand.net/askai/?q=What%27s+Treasure+In+Treasure+Out+-+as+opposed+to+Garbage+In+Garbage+Out%3F+Give+it+to+me+in+a+sentence.&#34;&gt;Treasure In Treasure Out&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;VS Code&amp;rsquo;s GitHub Copilot extension supports a &lt;code&gt;github.copilot.chat.commitMessageGeneration.instructions&lt;/code&gt; setting that lets you add a &lt;code&gt;[{&amp;quot;text&amp;quot;: ...}]&lt;/code&gt; or &lt;code&gt;[{&amp;quot;file&amp;quot;: &amp;quot;path/to/file.ext&amp;quot;}]&lt;/code&gt; prompt to the commit message generation. I&amp;rsquo;ve pointed this to my &lt;a href=&#34;https://github.com/sanand0/scripts/blob/main/agents/custom-prompts/git-commit.md&#34;&gt;&lt;code&gt;git-commit.md&lt;/code&gt;&lt;/a&gt; custom prompt.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
  </channel>
</rss>
