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.
The result is VS Code Scene, a lightweight extension that adds a subtle animated scene to the bottom of the Explorer panel.
π¬ Preview
βοΈ SkyPilot
π Aquarium
π Stars
What it does
It currently includes three scenes:
- SkyPilot β colorful airplanes (blue, green, red, and yellow) flying through soft, procedurally generated clouds with spinning propellers
- Aquarium β six cartoon fish swimming around with animated wagging tails, blowing bubbles, with a tiled background and gentle shimmering caustics
- Stars β a peaceful starfield with 115 twinkling stars (90 tiny background stars + 25 vivid five-pointed stars) slowly drifting upward
You can switch between them instantly from the status-bar icon (a color wheel in the bottom-right) or the command palette.
How to use
- Open the Explorer view β the Scene panel appears at the bottom automatically.
- Use the Command Palette (
Ctrl+Shift+P) or click the color-wheel icon in the status bar to switch scenes.
| Command | Description |
|---|---|
VS Code Scene: Start |
Opens the Explorer and shows the Scene panel |
VS Code Scene: Stop |
Closes the Explorer and hides the Scene |
VS Code Scene: Select Scene |
Opens the scene picker |
Scenes update live β no reload, no restart. The currently active scene is marked with a checkmark in the picker.
Technical approach
Pure Canvas2D, zero dependencies
Everything is rendered with the native Canvas2D 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.
A strict Content Security Policy
The webview runs under a strict CSP with a per-view nonce:
-
default-src 'none'β everything not listed is blocked by default -
script-src/style-srconly allow the extension's own inline scripts/styles bearing the nonce -
img-srcallows extension assets plusdata:URIs (needed for the Aquarium's sprite-splitting viacanvas.toDataURL())
No tracking, no analytics, no external requests β ever.
Animations pause when hidden
All animation is driven by the requestAnimationFrame 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.
Timing driven by rAF, not timers
One subtle bug I hit early on: using setTimeout / setInterval 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 update(deltaTime) with an accumulated counter, and each frame's delta is clamped to ~250 ms so a giant frame can never fire queued spawns.
One bundle, no CSP violations
The webpack config forces everything into a single bundle (splitChunks: false + dynamicImportMode: 'eager'). Async chunks would be injected as <script> tags without the CSP nonce and get blocked β causing a black screen. Keeping one bundle keeps both the CSP happy and the load fast.
Live scene switching
Scene switching is fully automatic through a chain:
- The
selectScenecommand updates thevscode-scene.screensetting. - The host's
ScenePanel.tslistens for configuration changes and posts the new scene type to the webview. - The webview disposes the old screen, creates a brand-new canvas element, and instantiates the new screen.
Replacing the whole canvas element guarantees zero leftover renderer state between switches.
Tips for VS Code WebViews
Building this taught me a few WebView-specific gotchas worth sharing:
- Prefer Canvas2D over WebGL. 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.
-
Assets need webview URIs. A screen can't use a plain filesystem path; every PNG is loaded through
window.__ASSETS_BASE_URI__(the host converts the extension'sassets/folder into a webview URI). -
Async
start()handling.BaseScreen.start()can returnvoidor aPromise; callers must checkinstanceof Promisebefore chaining.catch(). -
Guard async loading against disposal. Every screen checks a
disposedflag after eachawaitso a screen that's been replaced mid-load never touches the canvas.
Installation
Install it from the VS Code Marketplace:
ext install mh-farzin.vscode-scene
Or search VS Code Scene in the Extensions view (Ctrl+Shift+X).
Why I built it
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.
The whole thing is free and open source (MIT). A big thank-you to Kenney and Freepik for the game assets and graphics used in the scenes.
Links
- Marketplace: mh-farzin.vscode-scene
- GitHub: github.com/mhfarzin/vscode-scene
- Issues & Feedback: https://github.com/mhfarzin/vscode-scene/issues
Iβd love to hear your thoughts:
- Would you keep something like this enabled while coding?
- Which scene do you like most?
- What kind of scene would you want to see next?
Any feedback is welcome β even if itβs just βnot for me.β



Top comments (0)