<?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: shengqiang wang</title>
    <description>The latest articles on DEV Community by shengqiang wang (@shengqiang_wang_4fa8bd011).</description>
    <link>https://dev.to/shengqiang_wang_4fa8bd011</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%2F4032920%2F3c008981-36ba-431d-a2c3-dcce55684951.png</url>
      <title>DEV Community: shengqiang wang</title>
      <link>https://dev.to/shengqiang_wang_4fa8bd011</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shengqiang_wang_4fa8bd011"/>
    <language>en</language>
    <item>
      <title>How I Built a Browser-Based SVG Editor With Bidirectional Code-Canvas Sync</title>
      <dc:creator>shengqiang wang</dc:creator>
      <pubDate>Fri, 17 Jul 2026 01:32:29 +0000</pubDate>
      <link>https://dev.to/shengqiang_wang_4fa8bd011/how-i-built-a-browser-based-svg-editor-with-bidirectional-code-canvas-sync-4fhk</link>
      <guid>https://dev.to/shengqiang_wang_4fa8bd011/how-i-built-a-browser-based-svg-editor-with-bidirectional-code-canvas-sync-4fhk</guid>
      <description>&lt;p&gt;Every SVG editor I tried forced me into one workflow. Either you edit raw code and squint at a static preview, or you drag shapes around visually and the code is buried somewhere you never touch. I wanted both, so I built SVGDO (svgdo.com) — a browser-based SVG editor where code and canvas stay in lockstep.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft4ryk054gh1o90p0v65f.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft4ryk054gh1o90p0v65f.png" alt=" " width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this post, I'll walk through the three hardest technical problems I solved along the way.&lt;/p&gt;

&lt;p&gt;Problem 1: Making Code and Canvas Talk to Each Other&lt;/p&gt;

&lt;p&gt;Most SVG editors pick a lane. Code-first tools give you a text area and a preview iframe. Visual tools give you a canvas and bury the markup in a sidebar you never open. Bidirectional sync means neither side is the "source of truth" — both are live and either can drive changes.&lt;/p&gt;

&lt;p&gt;The core insight is that you need stable element identification across both representations. When the user loads an SVG on svgdo.com, the system parses it with DOMParser and walks the DOM tree, injecting a unique identifier onto every renderable element — paths, rects, circles, groups, text, and so on. Each element also gets recorded in a flat array with its tag name, all its attributes, and an index in the tree walk order.&lt;/p&gt;

&lt;p&gt;This gives you a bridge. Click an element on the canvas, read its identifier, find it in the array, and populate the properties panel with its current fill, stroke, and transform values. Modify a property in the panel, update the corresponding attribute on the element in the parsed DOM tree, re-serialize the whole SVG back to code with XMLSerializer, and push the result to the code editor and undo stack. All of this happens synchronously within a single frame, so the user sees the canvas and the code update together with no perceptible lag.&lt;/p&gt;

&lt;p&gt;The trickiest part is handling style attributes. SVG elements can carry fill and stroke both as standalone attributes and inside an inline style string. If you update the fill attribute but the element also has fill in its style string, the style wins. So the update logic has to check both places — set the attribute, but also strip it from the style string if it exists there. Otherwise your changes silently don't apply, which makes the editor look broken. I learned this the hard way when testing with SVGs exported from Figma, which heavily uses inline styles.&lt;/p&gt;

&lt;p&gt;Problem 2: Dragging Elements With Correct Coordinate Transform&lt;/p&gt;

&lt;p&gt;SVG uses its own internal coordinate system. A point that's at screen position (300, 200) might be at SVG coordinate (150, 120) depending on the viewBox, zoom level, and any parent transforms. If you naively map screen pixels to SVG coordinates, your drags will drift — especially when the canvas is zoomed.&lt;/p&gt;

&lt;p&gt;The fix is SVGPoint.matrixTransform with the inverse of the element's parent transformation matrix. Every SVG element has a method called getScreenCTM that returns the current transformation matrix mapping the element's local coordinates to screen coordinates. Invert that matrix, multiply it against the mouse's screen position, and you get the exact position in the element's own coordinate space. I spent an embarrassing amount of time debugging drag behavior before realizing the root cause was using the svg element's CTM instead of the parent group's CTM for nested elements.&lt;/p&gt;

&lt;p&gt;Once you have the delta between where the user clicked and where they dragged to, you express it as a translate transform on the element. On pointer up, you commit the accumulated transform back to the SVG source code. The key design decision: don't try to modify the element's native x, y, or d attributes. Use the transform attribute as a layer on top. This way, the element's original geometry stays intact, and you can always strip the transform to get back to the starting state — which means undo is trivially just restoring the previous transform value.&lt;/p&gt;

&lt;p&gt;Problem 3: SVG Optimization That Actually Saves Space&lt;/p&gt;

&lt;p&gt;SVG files exported from Illustrator, Figma, and Sketch contain a shocking amount of bloat. Comments, editor metadata, redundant xmlns declarations, and excessive numeric precision. A path coordinate like 123.456789 is visually identical to 123.457 at any reasonable display scale, but design tools default to maximum precision everywhere.&lt;/p&gt;

&lt;p&gt;I built two optimization tiers for svgdo.com. Safe mode strips comments, normalizes whitespace in path data, and removes editor-specific attributes. Aggressive mode goes further: strips all id and data attributes, removes empty group tags that served some purpose during design but are useless in the final file, truncates floating-point precision from six decimal places to three, and collapses all inter-tag whitespace. The precision reduction alone can shave 15 to 20 percent off a typical file.&lt;/p&gt;

&lt;p&gt;In testing with real SVGs exported from popular design tools, safe mode typically saves 10 to 30 percent, and aggressive mode can hit 40 to 60 percent. The tradeoff is that aggressive mode removes information you might want if you plan to re-edit the file in a visual tool later. That's why both modes exist and the original code is always kept in memory — one click restores everything. The byte savings are displayed in real time so users can make an informed choice rather than blindly trusting an optimizer.&lt;/p&gt;

&lt;p&gt;What I Learned&lt;/p&gt;

&lt;p&gt;The browser's built-in SVG tooling is surprisingly powerful. DOMParser and XMLSerializer handle parsing and serialization. SVGPoint and getScreenCTM provide the coordinate math. TreeWalker traverses the DOM efficiently. These are standard APIs that have been in browsers for years, and combined properly they cover 90 percent of what you need for a functional SVG editor.&lt;/p&gt;

&lt;p&gt;The remaining 10 percent is UX. Making drag feel natural on a touchpad. Handling edge cases like deeply nested groups where transforms multiply in ways the user doesn't expect. Not breaking the undo stack when the user rapidly toggles an optimization mode. These are the problems you only discover by building something real, putting it online at a real URL, and using it yourself.&lt;/p&gt;

&lt;p&gt;SVGDO is live at &lt;a href="https://dev.tourl"&gt;https://svgdo.com&lt;/a&gt; — it includes a 280-plus icon library, keyboard shortcuts for everything, five languages, and multi-format export. Fully open source on GitHub.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tutorial</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How I Built a Privacy-First Image Processing Toolbox That Runs Entirely in the Browser</title>
      <dc:creator>shengqiang wang</dc:creator>
      <pubDate>Fri, 17 Jul 2026 01:29:50 +0000</pubDate>
      <link>https://dev.to/shengqiang_wang_4fa8bd011/how-i-built-a-privacy-first-image-processing-toolbox-that-runs-entirely-in-the-browser-1lg7</link>
      <guid>https://dev.to/shengqiang_wang_4fa8bd011/how-i-built-a-privacy-first-image-processing-toolbox-that-runs-entirely-in-the-browser-1lg7</guid>
      <description>&lt;p&gt;How I Built a Privacy-First Image Processing Toolbox That Runs Entirely in the Browser&lt;/p&gt;

&lt;p&gt;I've always been frustrated by online image tools that quietly upload your photos to their servers. You drag in a family photo to resize it, and for all you know, it's now sitting on some random server. So I built my own solution: PictKit (pictkit.com), a free toolbox with 27 image tools where every operation stays in the browser.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffo564cvfvnql1sj88911.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffo564cvfvnql1sj88911.png" alt=" " width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this post, I'll walk through the core techniques that make client-side image processing possible, using real problems I hit while building it.&lt;/p&gt;

&lt;p&gt;The Architecture: Web Workers + OffscreenCanvas&lt;/p&gt;

&lt;p&gt;The key to keeping image processing fast and non-blocking is moving heavy work off the main thread. The browser's main thread handles UI rendering. If you hog it with image decoding and encoding, the entire page freezes -- buttons don't click, scrolling stutters, everything feels broken.&lt;/p&gt;

&lt;p&gt;Web Workers solve this. They run JavaScript in a completely separate thread with no access to the DOM. For image work, we pair them with OffscreenCanvas. As the name suggests, it's a canvas that doesn't exist anywhere on the page but supports all the same drawing operations as a regular canvas. The combination means you can decode an uploaded image, resize it, and re-encode it as WebP or JPEG, all without the main thread ever touching those bytes.&lt;/p&gt;

&lt;p&gt;OffscreenCanvas.convertToBlob is the hidden gem here. Before this API existed, you had to transfer raw pixel data back to the main thread just to encode it. That transfer was a bottleneck for large images -- megabytes of pixel data have to be copied (or transferred, which zeros the source buffer) between threads. Now, the worker can do the entire pipeline internally and only send back the compressed blob, which is typically a fraction of the size. When I was optimizing the compression tool on pictkit.com to handle 50MB photos without the UI freezing, this single API change made the biggest difference.&lt;/p&gt;

&lt;p&gt;createImageBitmap deserves a mention too. It handles image decoding off the main thread. Unlike the traditional approach of creating an &lt;code&gt;img&lt;/code&gt; element and waiting for its load event, createImageBitmap returns a bitmap directly, decodes in parallel, and integrates seamlessly with canvas contexts. Every millisecond you save on the main thread is a millisecond the UI stays responsive.&lt;/p&gt;

&lt;p&gt;Handling Aspect Ratio When Resizing&lt;/p&gt;

&lt;p&gt;A subtle detail that a lot of online tools get wrong: when you give a max width and max height, you can either force the image to those exact dimensions (which stretches it), or you can maintain the aspect ratio and fit the image into a bounding box.&lt;/p&gt;

&lt;p&gt;The correct approach is to calculate a uniform scale factor. Take the width ratio and height ratio, pick whichever is smaller, and apply it to both dimensions. This ensures the image never exceeds the target bounds and never gets distorted. Simple math, but surprising how many tools get the stretch behavior wrong by default. At pictkit.com, I made "keep aspect ratio" the default rather than an opt-in checkbox, because stretching is almost never what users actually want.&lt;/p&gt;

&lt;p&gt;Binary Search for Target File Size&lt;/p&gt;

&lt;p&gt;Users don't think in terms of "quality 0.72." They think "compress this to under 500KB." But quality-to-size isn't linear, so a fixed quality value is always guesswork.&lt;/p&gt;

&lt;p&gt;The solution is binary search. Encode at quality 50, check the size. Too big? Try 25. Too small? Try 75. Within about ten iterations you converge on the quality value that produces the closest match to the target file size. A 5% tolerance lets you early-exit when you're close enough. This approach works because the quality-to-size curve is monotonic -- lower quality always produces smaller files -- so binary search is guaranteed to converge. I added this as a "target file size" mode on the compressor, and it consistently lands within 5% of the user's goal on the first attempt.&lt;/p&gt;

&lt;p&gt;A Heads-Up About PNG Quality&lt;/p&gt;

&lt;p&gt;Here's something that trips people up: browsers completely ignore the quality parameter when encoding PNG. PNG is lossless by definition, so the encoder doesn't have a quality slider. Calling &lt;code&gt;canvas.toBlob&lt;/code&gt; with type &lt;code&gt;image/png&lt;/code&gt; and quality 0.5 produces the exact same file as quality 1.0. If you genuinely need to compress PNG files, you need a library that does quantization and color palette reduction, like browser-image-compression, which is what I ended up integrating for the PNG compression path on the site.&lt;/p&gt;

&lt;p&gt;SVG Security Without a Backend&lt;/p&gt;

&lt;p&gt;PictKit also includes an SVG editor, which means users can paste arbitrary SVG code from anywhere on the internet. SVGs are a surprisingly dangerous format -- they support &lt;code&gt;script&lt;/code&gt; tags, inline event handlers like &lt;code&gt;onclick&lt;/code&gt; and &lt;code&gt;onload&lt;/code&gt;, and even &lt;code&gt;foreignObject&lt;/code&gt; which embeds arbitrary HTML. Browsers don't execute scripts in &lt;code&gt;img&lt;/code&gt; tags, but they absolutely will if you inject SVG directly into the DOM via &lt;code&gt;innerHTML&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;DOMPurify handles this cleanly. It parses the SVG, strips anything dangerous, and returns sanitized markup. The important practice is being explicit about both allowlisted tags and forbidden ones. SVG has a huge surface area, and defense in depth -- allowing what you need while explicitly blocking what's dangerous -- is the safest posture.&lt;/p&gt;

&lt;p&gt;The Reason This Matters&lt;/p&gt;

&lt;p&gt;Every time you use an online image tool that processes files on a server, that server has a copy of your image. Maybe they delete it after processing. Maybe they don't. You have no way to know.&lt;/p&gt;

&lt;p&gt;Client-side processing eliminates that trust question entirely. The file never leaves your machine. The implications go beyond personal privacy -- think about lawyers redacting sensitive documents, doctors reviewing medical images, or anyone working with confidential material. For them, server-side processing isn't just a privacy concern, it's a compliance violation. That's the entire reason I built this the way I did.&lt;/p&gt;

&lt;p&gt;If you want to see all of this in action, the full toolbox is at pictkit.com -- compression, format conversion, color adjustment, watermarking, QR codes, and about 20 more tools, all running locally in the browser. Open source on GitHub too.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
