<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Electron on ICE-ICE-BEAR-BLOG</title><link>https://ice-ice-bear.github.io/tags/electron/</link><description>Recent content in Electron on ICE-ICE-BEAR-BLOG</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Mon, 29 Jun 2026 00:00:00 +0900</lastBuildDate><atom:link href="https://ice-ice-bear.github.io/tags/electron/index.xml" rel="self" type="application/rss+xml"/><item><title>Two Roads to AI Image Upscaling: A Desktop App vs an In-Browser Library</title><link>https://ice-ice-bear.github.io/posts/2026-06-29-ai-image-upscaling/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0900</pubDate><guid>https://ice-ice-bear.github.io/posts/2026-06-29-ai-image-upscaling/</guid><description>&lt;img src="https://ice-ice-bear.github.io/" alt="Featured image of post Two Roads to AI Image Upscaling: A Desktop App vs an In-Browser Library" /&gt;&lt;h2 id="overview"&gt;Overview
&lt;/h2&gt;&lt;p&gt;Two open-source projects solve the same problem — enlarge and enhance low-resolution images with AI — from opposite ends. &lt;a class="link" href="https://github.com/upscayl/upscayl" target="_blank" rel="noopener"
 &gt;Upscayl&lt;/a&gt; (46.5k★) is a polished cross-platform desktop app; &lt;a class="link" href="https://github.com/thekevinscott/upscalerjs" target="_blank" rel="noopener"
 &gt;UpscalerJS&lt;/a&gt; (890★) is a JavaScript library that runs in the browser or Node. Both lean on the ESRGAN family of super-resolution models, but the delivery vehicle shapes everything about who uses them and how.&lt;/p&gt;
&lt;pre class="mermaid" style="visibility:hidden"&gt;graph TD
 IMG["Low-res image"] --&gt; ESRGAN["ESRGAN-family super-resolution model"]
 ESRGAN --&gt; UP["Upscayl: Electron + Vulkan GPU (desktop)"]
 ESRGAN --&gt; JS["UpscalerJS: TensorFlow.js (browser / Node)"]
 UP --&gt; OUT1["Enhanced image, native app"]
 JS --&gt; OUT2["Enhanced image, in-app / web"]&lt;/pre&gt;&lt;hr&gt;
&lt;h2 id="upscayl-the-desktop-powerhouse"&gt;Upscayl: The Desktop Powerhouse
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://github.com/upscayl/upscayl" target="_blank" rel="noopener"
 &gt;Upscayl&lt;/a&gt; is the &amp;ldquo;#1 free and open-source AI image upscaler&amp;rdquo; for Linux, macOS, and Windows, and the 46,480 stars reflect how well the desktop-app formula works for this use case. It&amp;rsquo;s built in &lt;strong&gt;TypeScript on Electron&lt;/strong&gt;, ships through every channel that matters (Flathub, AppImage, AUR, Snap on Linux; Mac App Store and Homebrew &lt;code&gt;brew install --cask upscayl&lt;/code&gt; on macOS), and the distribution breadth is itself a feature — non-technical users can install it the way they install anything else.&lt;/p&gt;
&lt;p&gt;The one hard requirement is a &lt;strong&gt;Vulkan-compatible GPU&lt;/strong&gt;, which signals the architecture: this runs the upscaling models natively against your graphics card, not in a sandbox. That&amp;rsquo;s what lets it handle gigapixel-scale enlargement (its topics list reads &lt;code&gt;gigapixel&lt;/code&gt;, &lt;code&gt;esrgan&lt;/code&gt;, &lt;code&gt;topaz&lt;/code&gt; — positioning it as a free alternative to paid tools like Topaz Gigapixel). The recent commit log is mostly the unglamorous maintenance that keeps a mass-market app healthy: localization additions (Polish), electron-updater fixes, README and language-switcher polish. That&amp;rsquo;s the tax of serving a large non-developer audience well.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="upscalerjs-super-resolution-as-a-dependency"&gt;UpscalerJS: Super-Resolution as a Dependency
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://github.com/thekevinscott/upscalerjs" target="_blank" rel="noopener"
 &gt;UpscalerJS&lt;/a&gt; attacks the same problem as a &lt;strong&gt;library, not an app&lt;/strong&gt;. It&amp;rsquo;s MIT-licensed, browser- and Node-compatible, and built on &lt;strong&gt;TensorFlow.js&lt;/strong&gt;. The entire API surface is small enough to fit in a snippet:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Upscaler&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;upscaler&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;upscaler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Upscaler&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;upscaler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;upscale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/path/to/image&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;upscaledImage&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;upscaledImage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// base64 representation of image src
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That &lt;code&gt;new Upscaler().upscale(...)&lt;/code&gt; ergonomics is the whole pitch: super-resolution becomes a dependency you &lt;code&gt;npm install&lt;/code&gt;, not a program your users launch. It ships &lt;strong&gt;pretrained models&lt;/strong&gt; for different jobs — not just resolution increase but denoising, deblurring, dehazing, deraining, low-light enhancement, retouching (the topics list is a catalog of restoration tasks) — and supports custom model integration. A notable engineering detail is &lt;strong&gt;patch-based processing&lt;/strong&gt;: images are upscaled in tiles so the UI stays responsive and large images don&amp;rsquo;t blow up memory, which matters a lot when you&amp;rsquo;re running inference on the user&amp;rsquo;s own device inside a web page.&lt;/p&gt;
&lt;p&gt;The trade-offs are the mirror image of Upscayl&amp;rsquo;s. UpscalerJS reaches anywhere JavaScript runs — embed it in a web app and users upscale without installing anything — but it&amp;rsquo;s bounded by TensorFlow.js performance and the browser&amp;rsquo;s compute, so it won&amp;rsquo;t match a native Vulkan app on gigapixel work. Its recent commits (pinning Node 20–22, moving &lt;code&gt;shared/&lt;/code&gt; to &lt;code&gt;core/&lt;/code&gt;, Dependabot cooldowns) are the maintenance signature of a &lt;em&gt;library&lt;/em&gt; — dependency hygiene and module boundaries, not installers and localization.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="insights"&gt;Insights
&lt;/h2&gt;&lt;p&gt;The same model family, two delivery decisions, two entirely different products. &lt;strong&gt;Upscayl optimizes for the end user&lt;/strong&gt;: install it, point it at a folder, get gigapixel results — at the cost of requiring a capable GPU and platform-specific packaging. &lt;strong&gt;UpscalerJS optimizes for the developer&lt;/strong&gt;: drop it into any JS project and ship upscaling as a feature — at the cost of browser-bound performance. The split is a clean reminder that in applied AI, the model is rarely the differentiator; the &lt;em&gt;delivery surface&lt;/em&gt; is. Choosing between a native app with GPU access and a portable library running on TensorFlow.js is really a choice about who your user is and where the compute lives — and that decision cascades into everything from distribution channels to the shape of your commit log.&lt;/p&gt;</description></item></channel></rss>