<?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: Mh.Farzin</title>
    <description>The latest articles on DEV Community by Mh.Farzin (@mh_farzin).</description>
    <link>https://dev.to/mh_farzin</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%2F4078935%2F0b58a4f9-aa9f-4363-84a5-61cbe19a1caf.jpg</url>
      <title>DEV Community: Mh.Farzin</title>
      <link>https://dev.to/mh_farzin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mh_farzin"/>
    <language>en</language>
    <item>
      <title>I built a small ambient animation extension for the VS Code Explorer sidebar</title>
      <dc:creator>Mh.Farzin</dc:creator>
      <pubDate>Sat, 15 Aug 2026 13:44:36 +0000</pubDate>
      <link>https://dev.to/mh_farzin/i-built-a-small-ambient-animation-extension-for-the-vs-code-explorer-sidebar-36l7</link>
      <guid>https://dev.to/mh_farzin/i-built-a-small-ambient-animation-extension-for-the-vs-code-explorer-sidebar-36l7</guid>
      <description>&lt;p&gt;I spend a lot of time in VS Code. At some point I realized the Explorer sidebar felt a bit empty and static, so I decided to build something small that could make it feel a little more alive — without being distracting.&lt;/p&gt;

&lt;p&gt;The result is &lt;strong&gt;VS Code Scene&lt;/strong&gt;, a lightweight extension that adds a subtle animated scene to the bottom of the Explorer panel.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎬 Preview
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✈️ SkyPilot
&lt;/h3&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%2Fpf6g2awo8hxgcn71x0eu.gif" 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%2Fpf6g2awo8hxgcn71x0eu.gif" alt="SkyPilot" width="343" height="191"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🐠 Aquarium
&lt;/h3&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%2Fyq9b9u4arzdojlnbreb9.gif" 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%2Fyq9b9u4arzdojlnbreb9.gif" alt="Aquarium" width="347" height="193"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🌟 Stars
&lt;/h3&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%2F1zdu72vbb7auocuj4ayn.gif" 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%2F1zdu72vbb7auocuj4ayn.gif" alt="Stars" width="344" height="192"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;It currently includes three scenes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SkyPilot&lt;/strong&gt; — colorful airplanes (blue, green, red, and yellow) flying through soft, procedurally generated clouds with spinning propellers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aquarium&lt;/strong&gt; — six cartoon fish swimming around with animated wagging tails, blowing bubbles, with a tiled background and gentle shimmering caustics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stars&lt;/strong&gt; — a peaceful starfield with 115 twinkling stars (90 tiny background stars + 25 vivid five-pointed stars) slowly drifting upward&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can switch between them instantly from the status-bar icon (a color wheel in the bottom-right) or the command palette.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open the &lt;strong&gt;Explorer&lt;/strong&gt; view — the &lt;strong&gt;Scene&lt;/strong&gt; panel appears at the bottom automatically.&lt;/li&gt;
&lt;li&gt;Use the Command Palette (&lt;code&gt;Ctrl+Shift+P&lt;/code&gt;) or click the color-wheel icon in the status bar to switch scenes.&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;VS Code Scene: Start&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Opens the Explorer and shows the Scene panel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;VS Code Scene: Stop&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Closes the Explorer and hides the Scene&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;VS Code Scene: Select Scene&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Opens the scene picker&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Scenes update live — no reload, no restart. The currently active scene is marked with a checkmark in the picker.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical approach
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pure Canvas2D, zero dependencies
&lt;/h3&gt;

&lt;p&gt;Everything is rendered with the native &lt;strong&gt;Canvas2D&lt;/strong&gt; API. There are no external rendering libraries — no Three.js, no Pixi.js, no WebGL. The webview bundle is very small (~25 KB gzipped), and it's the only JavaScript your editor has to load.&lt;/p&gt;

&lt;h3&gt;
  
  
  A strict Content Security Policy
&lt;/h3&gt;

&lt;p&gt;The webview runs under a strict &lt;strong&gt;CSP&lt;/strong&gt; with a per-view nonce:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;default-src 'none'&lt;/code&gt; — everything not listed is blocked by default&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;script-src&lt;/code&gt; / &lt;code&gt;style-src&lt;/code&gt; only allow the extension's own inline scripts/styles bearing the nonce&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;img-src&lt;/code&gt; allows extension assets plus &lt;code&gt;data:&lt;/code&gt; URIs (needed for the Aquarium's sprite-splitting via &lt;code&gt;canvas.toDataURL()&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No tracking, no analytics, no external requests — ever.&lt;/p&gt;

&lt;h3&gt;
  
  
  Animations pause when hidden
&lt;/h3&gt;

&lt;p&gt;All animation is driven by the &lt;code&gt;requestAnimationFrame&lt;/code&gt; loop, so when the Explorer view is hidden or the window is minimized, the animation automatically pauses — keeping resource usage near zero when you're not looking at it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Timing driven by rAF, not timers
&lt;/h3&gt;

&lt;p&gt;One subtle bug I hit early on: using &lt;code&gt;setTimeout&lt;/code&gt; / &lt;code&gt;setInterval&lt;/code&gt; for spawn timers inside a screen caused a burst of objects to appear all at once when restoring a minimized window (timers keep firing in the background while rAF is paused). Every spawn/cooldown timer is now driven from &lt;code&gt;update(deltaTime)&lt;/code&gt; with an accumulated counter, and each frame's delta is clamped to ~250 ms so a giant frame can never fire queued spawns.&lt;/p&gt;

&lt;h3&gt;
  
  
  One bundle, no CSP violations
&lt;/h3&gt;

&lt;p&gt;The webpack config forces everything into a &lt;strong&gt;single bundle&lt;/strong&gt; (&lt;code&gt;splitChunks: false&lt;/code&gt; + &lt;code&gt;dynamicImportMode: 'eager'&lt;/code&gt;). Async chunks would be injected as &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags without the CSP nonce and get blocked — causing a black screen. Keeping one bundle keeps both the CSP happy and the load fast.&lt;/p&gt;

&lt;h3&gt;
  
  
  Live scene switching
&lt;/h3&gt;

&lt;p&gt;Scene switching is fully automatic through a chain:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;selectScene&lt;/code&gt; command updates the &lt;code&gt;vscode-scene.screen&lt;/code&gt; setting.&lt;/li&gt;
&lt;li&gt;The host's &lt;code&gt;ScenePanel.ts&lt;/code&gt; listens for configuration changes and posts the new scene type to the webview.&lt;/li&gt;
&lt;li&gt;The webview disposes the old screen, creates a &lt;strong&gt;brand-new canvas element&lt;/strong&gt;, and instantiates the new screen.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Replacing the whole canvas element guarantees zero leftover renderer state between switches.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tips for VS Code WebViews
&lt;/h3&gt;

&lt;p&gt;Building this taught me a few WebView-specific gotchas worth sharing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prefer Canvas2D over WebGL.&lt;/strong&gt; WebGL support in VS Code WebViews can be limited or unreliable depending on the environment, so pure Canvas2D is the safer and more compatible choice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assets need webview URIs.&lt;/strong&gt; A screen can't use a plain filesystem path; every PNG is loaded through &lt;code&gt;window.__ASSETS_BASE_URI__&lt;/code&gt; (the host converts the extension's &lt;code&gt;assets/&lt;/code&gt; folder into a webview URI).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Async &lt;code&gt;start()&lt;/code&gt; handling.&lt;/strong&gt; &lt;code&gt;BaseScreen.start()&lt;/code&gt; can return &lt;code&gt;void&lt;/code&gt; or a &lt;code&gt;Promise&lt;/code&gt;; callers must check &lt;code&gt;instanceof Promise&lt;/code&gt; before chaining &lt;code&gt;.catch()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guard async loading against disposal.&lt;/strong&gt; Every screen checks a &lt;code&gt;disposed&lt;/code&gt; flag after each &lt;code&gt;await&lt;/code&gt; so a screen that's been replaced mid-load never touches the canvas.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;Install it from the VS Code Marketplace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ext &lt;span class="nb"&gt;install &lt;/span&gt;mh-farzin.vscode-scene
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or search &lt;strong&gt;VS Code Scene&lt;/strong&gt; in the Extensions view (&lt;code&gt;Ctrl+Shift+X&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I built it
&lt;/h2&gt;

&lt;p&gt;I mainly built it for myself. I wanted something calm and subtle running in the background while coding — just a bit of quiet motion to make the sidebar feel less empty.&lt;/p&gt;

&lt;p&gt;The whole thing is free and open source (MIT). A big thank-you to &lt;strong&gt;Kenney&lt;/strong&gt; and &lt;strong&gt;Freepik&lt;/strong&gt; for the game assets and graphics used in the scenes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Marketplace&lt;/strong&gt;: &lt;a href="https://marketplace.visualstudio.com/items?itemName=mh-farzin.vscode-scene" rel="noopener noreferrer"&gt;mh-farzin.vscode-scene&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/mhfarzin/vscode-scene" rel="noopener noreferrer"&gt;github.com/mhfarzin/vscode-scene&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Issues &amp;amp; Feedback&lt;/strong&gt;: &lt;a href="https://github.com/mhfarzin/vscode-scene/issues" rel="noopener noreferrer"&gt;https://github.com/mhfarzin/vscode-scene/issues&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;I’d love to hear your thoughts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Would you keep something like this enabled while coding?&lt;/li&gt;
&lt;li&gt;Which scene do you like most?&lt;/li&gt;
&lt;li&gt;What kind of scene would you want to see next?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Any feedback is welcome — even if it’s just “not for me.”&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
