<?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: Wixard</title>
    <description>The latest articles on DEV Community by Wixard (@wizardtheturtle89237).</description>
    <link>https://dev.to/wizardtheturtle89237</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3828495%2F4ec9838e-c7b9-46e5-81be-c35d8f83f9e6.png</url>
      <title>DEV Community: Wixard</title>
      <link>https://dev.to/wizardtheturtle89237</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wizardtheturtle89237"/>
    <language>en</language>
    <item>
      <title>I Built a Sprite Sheet Tool That Runs Entirely in the Browser (No Uploads, No Backend)</title>
      <dc:creator>Wixard</dc:creator>
      <pubDate>Tue, 17 Mar 2026 04:12:51 +0000</pubDate>
      <link>https://dev.to/wizardtheturtle89237/i-built-a-sprite-sheet-tool-that-runs-entirely-in-the-browser-no-uploads-no-backend-18i3</link>
      <guid>https://dev.to/wizardtheturtle89237/i-built-a-sprite-sheet-tool-that-runs-entirely-in-the-browser-no-uploads-no-backend-18i3</guid>
      <description>&lt;h1&gt;
  
  
  I Built a Browser-Based Sprite Toolchain (Video → Atlas, Packing, Compression, No Backend)
&lt;/h1&gt;

&lt;p&gt;Most sprite workflows are fragmented.&lt;/p&gt;

&lt;p&gt;You end up using one tool to extract frames, another to pack atlases, another to compress, and something else to convert formats. On top of that, most tools require uploads or paid features.&lt;/p&gt;

&lt;p&gt;I wanted a single pipeline that handled everything, without a backend.&lt;/p&gt;

&lt;p&gt;So I built a browser-based sprite toolchain that runs entirely client-side and didnt charge an arm and a leg&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ilovesprites.com" rel="noopener noreferrer"&gt;https://ilovesprites.com&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.amazonaws.com%2Fuploads%2Farticles%2F0mz5qadt052j6py92rek.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.amazonaws.com%2Fuploads%2Farticles%2F0mz5qadt052j6py92rek.png" alt=" " width="800" height="503"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Does
&lt;/h2&gt;

&lt;p&gt;The tool covers the full sprite workflow:&lt;/p&gt;

&lt;h3&gt;
  
  
  Input and Conversion
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Convert video or GIF to a sprite sheet with configurable FPS and scale&lt;/li&gt;
&lt;li&gt;Convert video to GIF or MP4&lt;/li&gt;
&lt;li&gt;Extract frames from video or GIF and download as a ZIP&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Atlas and Packing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Pack multiple images into a single texture atlas&lt;/li&gt;
&lt;li&gt;Supports MaxRects, Shelf, and Grid packing&lt;/li&gt;
&lt;li&gt;Duplicate sprite detection (alias packing)&lt;/li&gt;
&lt;li&gt;Optional power-of-two sizing, padding, extrusion, and trimming&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Editing and Utilities
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Split sprite sheets into individual frames (grid or margin-based)&lt;/li&gt;
&lt;li&gt;Bulk resize sprites (scale or max constraints)&lt;/li&gt;
&lt;li&gt;Generate normal maps from height maps or images&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Output and Optimization
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Convert sprite sheets into animated GIFs&lt;/li&gt;
&lt;li&gt;Convert GIF to MP4 or WebM&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Compress GIFs with control over:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;color count&lt;/li&gt;
&lt;li&gt;dithering&lt;/li&gt;
&lt;li&gt;scaling&lt;/li&gt;
&lt;li&gt;FPS&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;p&gt;This is essentially a full asset pipeline implemented in the browser.&lt;/p&gt;




&lt;h3&gt;
  
  
  Video and GIF Processing
&lt;/h3&gt;

&lt;p&gt;For video and GIF decoding, the system uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FFmpeg.wasm for decoding and format conversion&lt;/li&gt;
&lt;li&gt;HTML5 video element and Canvas for frame extraction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FFmpeg.wasm handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;video to GIF/MP4 conversion&lt;/li&gt;
&lt;li&gt;GIF to video conversion&lt;/li&gt;
&lt;li&gt;frame extraction pipelines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Canvas is used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pixel-level access&lt;/li&gt;
&lt;li&gt;frame capture&lt;/li&gt;
&lt;li&gt;final atlas generation&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Frame Extraction
&lt;/h3&gt;

&lt;p&gt;Frames are extracted either via:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FFmpeg.wasm (for precise decoding pipelines)&lt;/li&gt;
&lt;li&gt;or video element + Canvas (for faster interactive workflows)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Key considerations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;accurate frame stepping&lt;/li&gt;
&lt;li&gt;handling variable framerate inputs&lt;/li&gt;
&lt;li&gt;avoiding frame duplication or skipping&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Image Processing Pipeline
&lt;/h3&gt;

&lt;p&gt;Once frames are extracted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Converted to ImageBitmap where possible&lt;/li&gt;
&lt;li&gt;Normalized to consistent dimensions&lt;/li&gt;
&lt;li&gt;Optionally trimmed to remove transparent bounds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This stage is optimized to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reduce memory allocations&lt;/li&gt;
&lt;li&gt;avoid unnecessary redraws&lt;/li&gt;
&lt;li&gt;keep processing responsive&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Atlas Packing
&lt;/h3&gt;

&lt;p&gt;Packing is handled using multiple strategies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MaxRects (primary)&lt;/li&gt;
&lt;li&gt;Shelf&lt;/li&gt;
&lt;li&gt;Grid&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MaxRects is used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;optimal space utilization&lt;/li&gt;
&lt;li&gt;irregular frame sizes&lt;/li&gt;
&lt;li&gt;minimizing wasted texture space&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additional features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;multi-atlas output when limits are exceeded&lt;/li&gt;
&lt;li&gt;padding and extrusion to prevent bleeding&lt;/li&gt;
&lt;li&gt;optional power-of-two constraints&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Duplicate Detection
&lt;/h3&gt;

&lt;p&gt;To reduce atlas size:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pixel data is hashed&lt;/li&gt;
&lt;li&gt;identical frames are reused instead of duplicated&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This enables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;smaller atlases&lt;/li&gt;
&lt;li&gt;more efficient memory usage&lt;/li&gt;
&lt;li&gt;faster runtime performance in engines&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Optimization Layer
&lt;/h3&gt;

&lt;p&gt;Several optimization passes are applied:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PNG quantization for reduced file size&lt;/li&gt;
&lt;li&gt;multi-resolution scaling&lt;/li&gt;
&lt;li&gt;trimming of transparent pixels&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are especially useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;mobile games&lt;/li&gt;
&lt;li&gt;web delivery&lt;/li&gt;
&lt;li&gt;memory-constrained environments&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Export System
&lt;/h3&gt;

&lt;p&gt;Exports are designed to plug directly into engines and frameworks:&lt;/p&gt;

&lt;p&gt;Supported formats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unity&lt;/li&gt;
&lt;li&gt;Godot&lt;/li&gt;
&lt;li&gt;Phaser&lt;/li&gt;
&lt;li&gt;PixiJS&lt;/li&gt;
&lt;li&gt;Spine&lt;/li&gt;
&lt;li&gt;Starling XML&lt;/li&gt;
&lt;li&gt;CSS sprites&lt;/li&gt;
&lt;li&gt;generic JSON&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Output includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;atlas image(s)&lt;/li&gt;
&lt;li&gt;metadata (positions, sizes, offsets)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Performance Strategy
&lt;/h3&gt;

&lt;p&gt;Running entirely in the browser introduces constraints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;limited memory&lt;/li&gt;
&lt;li&gt;device-dependent performance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To handle this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OffscreenCanvas is used where available&lt;/li&gt;
&lt;li&gt;buffers are reused&lt;/li&gt;
&lt;li&gt;operations are batched&lt;/li&gt;
&lt;li&gt;unnecessary allocations are avoided&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Privacy and Architecture
&lt;/h3&gt;

&lt;p&gt;All processing is done locally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no file uploads&lt;/li&gt;
&lt;li&gt;no server round trips&lt;/li&gt;
&lt;li&gt;no storage of user data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is enabled by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FFmpeg.wasm&lt;/li&gt;
&lt;li&gt;Canvas-based processing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;immediate results&lt;/li&gt;
&lt;li&gt;zero infrastructure cost&lt;/li&gt;
&lt;li&gt;full user privacy&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;generating sprite sheets from gameplay recordings&lt;/li&gt;
&lt;li&gt;preparing animation atlases&lt;/li&gt;
&lt;li&gt;optimizing assets for mobile or web&lt;/li&gt;
&lt;li&gt;converting between sprite formats and pipelines&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What I’m Looking For
&lt;/h2&gt;

&lt;p&gt;Feedback on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;additional export formats&lt;/li&gt;
&lt;li&gt;performance edge cases&lt;/li&gt;
&lt;li&gt;engine-specific improvements&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Unity package export&lt;/li&gt;
&lt;li&gt;animation preview inside the browser&lt;/li&gt;
&lt;li&gt;CLI version for build pipelines&lt;/li&gt;
&lt;li&gt;browser extension for extracting sprites&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://ilovesprites.com" rel="noopener noreferrer"&gt;https://ilovesprites.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you run into edge cases or have workflow suggestions, I’m interested in hearing them.&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>showdev</category>
      <category>tooling</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
