<?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: Karmanya Veer Sharma</title>
    <description>The latest articles on DEV Community by Karmanya Veer Sharma (@karmanya007).</description>
    <link>https://dev.to/karmanya007</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%2F486351%2F5aadb492-cd3b-4964-b126-b7a3e802f00a.png</url>
      <title>DEV Community: Karmanya Veer Sharma</title>
      <link>https://dev.to/karmanya007</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/karmanya007"/>
    <language>en</language>
    <item>
      <title>I rebuilt Wallpaper Engine in Rust</title>
      <dc:creator>Karmanya Veer Sharma</dc:creator>
      <pubDate>Wed, 28 Oct 2026 22:42:40 +0000</pubDate>
      <link>https://dev.to/karmanya007/i-rebuilt-wallpaper-engine-in-rust-ln5</link>
      <guid>https://dev.to/karmanya007/i-rebuilt-wallpaper-engine-in-rust-ln5</guid>
      <description>&lt;p&gt;Wallpaper Engine is honestly one of the best things on Steam. Millions of animated wallpapers, everything from plain looping videos to layered 2D scenes with particles, shaders and little scripts running inside them.&lt;/p&gt;

&lt;p&gt;It also has a few habits that bug me. It's Windows-only and closed source. The GPU never really idles. Maximize one window and every monitor pauses. And if an audio device disappears, video wallpapers can just break.&lt;/p&gt;

&lt;p&gt;So I got curious: how much of it could I rebuild as an open engine that still plays the content people already own?&lt;/p&gt;

&lt;p&gt;Two weeks later, I have an answer. It's called &lt;strong&gt;&lt;a href="https://github.com/karmanya007/Diorama" rel="noopener noreferrer"&gt;Diorama&lt;/a&gt;&lt;/strong&gt;, and it's about 36,000 lines of Rust on top of wgpu. It plays Wallpaper Engine scenes, videos and web wallpapers straight from your local Workshop folder, plus your own files. Right now it's Windows only. Linux is next.&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%2Fw2mrwv2c20q4rjw574h7.gif" 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%2Fw2mrwv2c20q4rjw574h7.gif" alt="Aurora, one of Diorama's example wallpapers" width="640" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This post is about how it works. But mostly it's about how I convinced myself that it actually works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule #1: the renderer never sees Wallpaper Engine
&lt;/h2&gt;

&lt;p&gt;The very first decision I made was architectural, and it's the one I'm happiest with. Wallpaper Engine's file formats are the importer's problem. Nothing else in the codebase even knows they exist.&lt;/p&gt;

&lt;p&gt;The split looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;diorama-we&lt;/code&gt;&lt;/strong&gt; reads all the Wallpaper Engine formats: &lt;code&gt;.pkg&lt;/code&gt; archives, &lt;code&gt;.tex&lt;/code&gt; textures (LZ4, DXT, sprite sheets, even embedded MP4s), &lt;code&gt;.mdl&lt;/code&gt; models and puppets, the scene/effect/material/particle JSON, and their custom shader dialect. It turns all of that into an engine-neutral scene format, &lt;code&gt;diorama-scene&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;diorama-render&lt;/code&gt;&lt;/strong&gt; only ever sees that neutral format: layers, materials, passes, effect chains, particles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;diorama-platform&lt;/code&gt;&lt;/strong&gt; is the one crate allowed to touch the OS.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I didn't trust myself to keep this clean, so a CI script enforces it. The build fails if a lower layer depends on a higher one, or if &lt;code&gt;cfg(windows)&lt;/code&gt; shows up anywhere outside the platform crate.&lt;/p&gt;

&lt;p&gt;This boundary paid for itself over and over. Every time I hit some new Wallpaper Engine quirk, the fix went into the importer and the renderer stayed simple.&lt;/p&gt;

&lt;p&gt;I also decided early that anything coming from the Workshop is hostile input:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Parsers&lt;/strong&gt; are bounds-checked, fuzzed, and never allowed to &lt;code&gt;unwrap&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scripts&lt;/strong&gt; run in QuickJS with memory and time limits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shaders&lt;/strong&gt; get validated before they're used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web wallpapers&lt;/strong&gt; run offline by default, each in its own process.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Shaders: GLSL dialect → HLSL → DXC → SPIR-V → naga → wgpu
&lt;/h2&gt;

&lt;p&gt;Yes, that heading is the actual pipeline.&lt;/p&gt;

&lt;p&gt;Wallpaper Engine's shaders are written in a GLSL-ish dialect with &lt;code&gt;#include&lt;/code&gt;s, &lt;code&gt;// [COMBO]&lt;/code&gt; permutation markers, JSON annotations on uniforms, and Direct3D 11 semantics sitting underneath all of it.&lt;/p&gt;

&lt;p&gt;So Diorama:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;preprocesses them,&lt;/li&gt;
&lt;li&gt;translates them to HLSL,&lt;/li&gt;
&lt;li&gt;compiles that with DXC to SPIR-V,&lt;/li&gt;
&lt;li&gt;validates and loads the result through naga into wgpu,&lt;/li&gt;
&lt;li&gt;and caches every permutation on disk.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Going through HLSL looks like a detour, and I didn't pick it on a hunch. I ran a quick spike on my test library: 330 of 336 shader/permutation pairs compile via HLSL, versus 299 going straight through GLSL. Easy call.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I checked it: RenderDoc captures and SSIM
&lt;/h2&gt;

&lt;p&gt;"Looks about right" isn't a test, and with a renderer it's very easy to fool yourself. So I went to the source of truth.&lt;/p&gt;

&lt;p&gt;I captured Wallpaper Engine itself with RenderDoc. A capture gives you every draw call, every render target and every uniform, including the exact &lt;code&gt;g_Time&lt;/code&gt; of that frame.&lt;/p&gt;

&lt;p&gt;On the Diorama side, I can render any scene off-screen at a specific time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dioramad &lt;span class="nt"&gt;--scene&lt;/span&gt; … &lt;span class="nt"&gt;--time&lt;/span&gt; t &lt;span class="nt"&gt;--frame-out&lt;/span&gt; x.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means every capture becomes a reference image. A small tool then scores the difference between the two with SSIM.&lt;/p&gt;

&lt;p&gt;The first scene I tried scored 0.996, which felt great. Then I ran it over all 55 scenes in my library:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;35&lt;/strong&gt; score 0.95 or higher&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;48&lt;/strong&gt; score 0.90 or higher&lt;/li&gt;
&lt;li&gt;the mean is &lt;strong&gt;0.948&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The captures were also the best documentation I had. They answered questions that no amount of reading would have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wallpaper Engine uses &lt;strong&gt;reversed-Z&lt;/strong&gt; depth with a GREATER test, and &lt;strong&gt;blended draws never write depth&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Layers marked "no interpolation" use point filtering &lt;strong&gt;the whole way through&lt;/strong&gt;: source texture, effect passes and the final composite. Miss one step and pixel-art scenes go soft.&lt;/li&gt;
&lt;li&gt;A layer's &lt;strong&gt;last effect pass draws straight into the scene&lt;/strong&gt;, not into another render target.&lt;/li&gt;
&lt;li&gt;Particle &lt;code&gt;count&lt;/code&gt; scales the &lt;strong&gt;emission rate&lt;/strong&gt;, not the maximum.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some differences I just can't match. One terrain shader builds its hash function on &lt;code&gt;sin&lt;/code&gt;, which compiles differently in FXC and DXC, so the pattern comes out different everywhere. I've made peace with that one.&lt;/p&gt;

&lt;h2&gt;
  
  
  SceneScript
&lt;/h2&gt;

&lt;p&gt;32 of my 55 scenes use SceneScript, Wallpaper Engine's JavaScript layer. It's what powers clocks, interactive layers and even small games inside wallpapers.&lt;/p&gt;

&lt;p&gt;The nice surprise here: Wallpaper Engine ships complete TypeScript typings for it. That gave me a clear spec to build against. Diorama implements the API in QuickJS (via rquickjs), covering layers, animations, cursor events, audio and media, user properties and timers.&lt;/p&gt;

&lt;p&gt;One scene full of draggable desktop "windows" took a while. Getting it right needed pointer capture, plus sending cursor events only to the topmost layer that's actually listening for them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web wallpapers: the case of the frozen weather widget
&lt;/h2&gt;

&lt;p&gt;Web wallpapers run in WebView2, each in its own process, and &lt;strong&gt;the network is off unless you allow it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That default gave me my favourite bug of the whole project.&lt;/p&gt;

&lt;p&gt;A weather widget was frozen solid on the desktop. But when I rendered it off-screen, it was perfectly fine. Same wallpaper, same code, completely different behaviour.&lt;/p&gt;

&lt;p&gt;The culprit was in its audio handler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;smoothedPoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;createVector&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;smoothedPoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;createVector&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="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&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;&lt;code&gt;createVector&lt;/code&gt; comes from p5.js, which the wallpaper loads from a CDN. Diorama had blocked that CDN. So &lt;code&gt;createVector&lt;/code&gt; was never a function, nothing ever got pushed, and the very first audio frame walked into a loop that could never end.&lt;/p&gt;

&lt;p&gt;And the off-screen renders? They don't get audio. No audio frame, no loop, no freeze.&lt;/p&gt;

&lt;p&gt;Diorama now reports blocked files and stuck pages, and gives you an "Allow internet access" switch for that specific wallpaper.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixing the things people complain about
&lt;/h2&gt;

&lt;p&gt;The other half of the project was working through a list of Wallpaper Engine pain points. Each one got its own test, so it stays fixed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Idle cost:&lt;/strong&gt; a static wallpaper stops drawing completely. 0.00% GPU when idle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pausing:&lt;/strong&gt; each monitor pauses on its own when there's a fullscreen app or maximized window in front of it. One maximized window no longer freezes everything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows 11 24H2:&lt;/strong&gt; it survives the new desktop window layout (including with icons hidden), Explorer restarts and graphics driver resets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audio:&lt;/strong&gt; video never depends on an audio device.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost report:&lt;/strong&gt; the library shows each wallpaper's measured CPU, GPU and memory, so you know what it costs before you leave it running all day.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Video decoding:&lt;/strong&gt; if your GPU can't decode a video, you get a one-click conversion instead of a CPU running hot.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Linux.&lt;/strong&gt; Wayland layer-shell and X11, VA-API video, PipeWire audio. This is where an open engine matters most, so it's my top priority.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Closing the remaining gaps,&lt;/strong&gt; mainly planar reflections and camera fades.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A signed installer.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Diorama is dual-licensed MIT/Apache-2.0. It ships no Wallpaper Engine code or content. It just reads what you already have.&lt;/p&gt;

&lt;p&gt;The code, an installer and three original example wallpapers are all at &lt;strong&gt;&lt;a href="https://github.com/karmanya007/Diorama" rel="noopener noreferrer"&gt;github.com/karmanya007/Diorama&lt;/a&gt;&lt;/strong&gt;. If a wallpaper looks wrong, please open an issue with its Workshop id. That helps a lot.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;A note on how this was built: I leaned heavily on an AI coding assistant (Claude Code). I steered the design, ran the captures and reviewed the work, and the commits carry its co-author line.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>showdev</category>
      <category>opensource</category>
      <category>graphics</category>
    </item>
  </channel>
</rss>
