<?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: popo suke</title>
    <description>The latest articles on DEV Community by popo suke (@ogataquin).</description>
    <link>https://dev.to/ogataquin</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%2F3800688%2Fd2cf3573-d992-43a6-a7b9-8f4c9d124a28.png</url>
      <title>DEV Community: popo suke</title>
      <link>https://dev.to/ogataquin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ogataquin"/>
    <language>en</language>
    <item>
      <title>Can AI Disassemble What AI Created? — Background Removal Stress Test in 8 Levels</title>
      <dc:creator>popo suke</dc:creator>
      <pubDate>Wed, 04 Mar 2026 08:02:59 +0000</pubDate>
      <link>https://dev.to/ogataquin/can-ai-disassemble-what-ai-created-background-removal-stress-test-in-8-levels-2a1d</link>
      <guid>https://dev.to/ogataquin/can-ai-disassemble-what-ai-created-background-removal-stress-test-in-8-levels-2a1d</guid>
      <description>&lt;p&gt;&lt;strong&gt;"AI background removal is amazing, right?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You've seen the comparison articles. A model standing in front of a white backdrop. Cleanly cut out. "Amazing!" End of article.&lt;/p&gt;

&lt;p&gt;Of course it is. A person on a white background? AI could do that in 2020.&lt;/p&gt;

&lt;p&gt;In this article, we take the test a little more seriously.&lt;/p&gt;

&lt;p&gt;We use &lt;strong&gt;Nano Banana 2&lt;/strong&gt; (Google's image generation AI) to create progressively more complex images, then feed them into a browser-based background removal AI.&lt;/p&gt;

&lt;p&gt;Full disclosure: the background removal tool used here, &lt;strong&gt;&lt;a href="https://www.imagetools-free.com/bg-remove" rel="noopener noreferrer"&gt;Image Tools&lt;/a&gt;&lt;/strong&gt;, is a web app I built myself. It runs AI inference entirely in the browser — no server uploads. Since it's my own product, I have no intention of going easy on it. I want to know exactly where it breaks. That's the whole point.&lt;/p&gt;

&lt;p&gt;The rules are simple. &lt;strong&gt;At what level does it hold up, and where does it collapse?&lt;/strong&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%2Fhhnu86r76h6b1xvq9703.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%2Fhhnu86r76h6b1xvq9703.png" alt=" " width="800" height="355"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The Image Tools background removal interface. Runs entirely in the browser — images are never sent to a server.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Rules
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Image generation: &lt;strong&gt;Nano Banana 2&lt;/strong&gt; (Google Gemini family, released February 2026)&lt;/li&gt;
&lt;li&gt;Background removal: &lt;strong&gt;&lt;a href="https://www.imagetools-free.com/bg-remove" rel="noopener noreferrer"&gt;Image Tools&lt;/a&gt;&lt;/strong&gt; (browser-only, no server upload)&lt;/li&gt;
&lt;li&gt;Evaluation: my eyes. No scientific rigor. The only question is "is this usable in practice?"&lt;/li&gt;
&lt;li&gt;One image per level. No retries. One shot.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Lv.1 — Warm-Up
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Challenge: White background × Simple subject&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nano Banana 2 prompt:&lt;br&gt;
&lt;code&gt;A single red apple on a pure white background, product photography, centered, studio lighting&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For a background removal AI, this is the equivalent of morning stretches. If it fails here, it should file for retirement.&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%2Fz3rlji0d6suarhq9when.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%2Fz3rlji0d6suarhq9when.png" alt=" " width="800" height="436"&gt;&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%2Fvomkketeqa3f0c3dl7o2.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%2Fvomkketeqa3f0c3dl7o2.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result: ⭐⭐⭐⭐⭐ Flawless&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The apple's edge, the stem, the leaf. Not a single pixel out of place. The shadow is gone without a trace.&lt;/p&gt;

&lt;p&gt;Well, of course. If we were impressed by this, the article would be over in eight lines. Let's move on.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lv.2 — The Everyday
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Challenge: Gradient background × Person&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nano Banana 2 prompt:&lt;br&gt;
&lt;code&gt;A woman in a dark navy business suit standing in front of a soft blue-to-purple gradient background, professional headshot, upper body&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The background upgrades from solid white to a gradient. Still, the subject is a person — the bread and butter of background removal AI. If it stumbles here, there's nothing but hell waiting ahead.&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%2Fx92kdr75xfm43021fko8.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%2Fx92kdr75xfm43021fko8.png" alt=" " width="800" height="1433"&gt;&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%2Fiwhhdz6kgariobrt14uu.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%2Fiwhhdz6kgariobrt14uu.png" alt=" " width="800" height="1433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result: ⭐⭐⭐⭐⭐ Flawless&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The gradient background vanishes completely. The wave of her hair, the line of her shoulders, the thin chain of her necklace — all preserved with surgical precision.&lt;/p&gt;

&lt;p&gt;I'd like to applaud, but this is still Lv.2. Praising the AI here is like telling a marathon runner "great form" at the 2km mark.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lv.3 — Getting Serious
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Challenge: Cluttered background × Subject&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nano Banana 2 prompt:&lt;br&gt;
&lt;code&gt;A tabby cat sitting on a wooden desk cluttered with books, pens, a coffee mug, and scattered papers, natural window lighting, realistic photo&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now we're talking. The background is full of objects. The cat is sitting directly on the desk. The real question: can the AI tell "background" from "foreground objects that aren't the main subject"?&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%2Fvzaihqxr7p267is18b7c.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%2Fvzaihqxr7p267is18b7c.png" alt=" " width="800" height="436"&gt;&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%2Fcgcx6f7xruvo5fhj36p5.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%2Fcgcx6f7xruvo5fhj36p5.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result: ⭐⭐⭐ An Unexpected Judgment Call&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The cat survived. Good.&lt;/p&gt;

&lt;p&gt;The problem is that &lt;strong&gt;the books and the coffee mug survived too.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The window, the desk surface, and the background wall are gone. But the objects surrounding the cat — a row of Orwell paperbacks, a mug labeled "STUDIO NOTES," scattered pens — linger on as semi-transparent ghosts.&lt;/p&gt;

&lt;p&gt;The AI apparently decided: "This cat and these objects are a package deal."&lt;/p&gt;

&lt;p&gt;The coffee mug was classified as the cat's ally. In fairness, the relationship between cats and mugs runs deep. In the history of humanity, the number of times a cat has pushed a mug off a desk likely reaches astronomical figures. One can understand why the AI refused to separate them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lv.4 — Camouflage
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Challenge: Subject and background are the same color&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nano Banana 2 prompt:&lt;br&gt;
&lt;code&gt;A white Persian cat sleeping on a white fluffy blanket on a white sofa, soft natural lighting, realistic photograph, slightly overexposed&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;White on white on white. For a background removal AI, this is like fighting an enemy in active camouflage. Color data is almost useless. Can it identify the subject by shape alone?&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%2Fit7t1s8mw1ass434lrzj.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%2Fit7t1s8mw1ass434lrzj.png" alt=" " width="800" height="436"&gt;&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%2Fkyasdafxjnd6aso5aqrk.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%2Fkyasdafxjnd6aso5aqrk.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result: ⭐⭐⭐⭐ Better than expected — and fascinating&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The sofa is gone. The cushions are gone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But the blanket remains.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The cat and blanket were extracted as a single unit. The boundary between sofa and blanket was cleanly severed, but the boundary between cat and blanket was not.&lt;/p&gt;

&lt;p&gt;To be fair, look at the original image again. &lt;strong&gt;Can you find the boundary between cat and blanket?&lt;/strong&gt; The Persian cat's long white fur sinks into the white faux fur throw. Where the cat ends and the blanket begins is unclear even to the human eye.&lt;/p&gt;

&lt;p&gt;The AI decided they couldn't be separated. And it's probably right. If it had tried, the cat's lower body would have vanished. The AI chose to rescue the cat by rescuing the blanket along with it — a remarkably pragmatic decision.&lt;/p&gt;

&lt;p&gt;Or perhaps the cat has simply fused with the blanket into an entirely new life form. That possibility cannot be ruled out.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lv.5 — The Hair Battle
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Challenge: Complex edges (curly hair, backlighting)&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nano Banana 2 prompt:&lt;br&gt;
&lt;code&gt;A woman with extremely curly voluminous afro hair, standing in a lush green garden with trees and bushes, golden hour sunlight filtering through her hair, realistic portrait photography&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The nemesis of background removal: hair. And not just any hair — backlit hair with sunlight leaking through every curl. At the pixel level, the boundary between hair and background simply does not exist.&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%2Fmn0veo4azxzngxv4sock.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%2Fmn0veo4azxzngxv4sock.png" alt=" " width="800" height="436"&gt;&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%2Fj6pi64b48lqjynfozxi4.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%2Fj6pi64b48lqjynfozxi4.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result: ⭐⭐⭐ A valiant effort, sabotaged by the sun&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The person is extracted successfully. Body line, clothing, face — all fine.&lt;/p&gt;

&lt;p&gt;But a &lt;strong&gt;golden halo&lt;/strong&gt; lingers around the hair. The backlight that filtered through each individual strand was classified as "part of the hair" rather than "background."&lt;/p&gt;

&lt;p&gt;Is sunlight background, or part of the hair?&lt;/p&gt;

&lt;p&gt;In physics, it's photons that passed through from behind. Visually, it's the element that makes the hair glow. The AI chose "when in doubt, keep it." Better to preserve a glow than to shred the edges of the hair.&lt;/p&gt;

&lt;p&gt;In practice, this works fine on a white or light background. Put it on a dark background, though, and a golden ghost will appear around the hair. A result that depends on its destination.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lv.6 — The Threshold of Philosophy
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Challenge: Transparent / semi-transparent subject&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nano Banana 2 prompt:&lt;br&gt;
&lt;code&gt;A clear glass wine glass half filled with white wine, sitting on a marble table with a colorful flower garden visible in the background, sharp focus, realistic product photography&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What does it mean to "remove the background" of something transparent?&lt;/p&gt;

&lt;p&gt;Is the garden visible through the glass part of the glass, or part of the background? Are the flowers refracted in the wine surface the subject, or the scenery? If the AI has an existential crisis at this point, no one can blame it.&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%2Fw3e3yn394vmh0gcb8a2i.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%2Fw3e3yn394vmh0gcb8a2i.png" alt=" " width="800" height="436"&gt;&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%2F6v4sq9sdggi75x8dlmk2.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%2F6v4sq9sdggi75x8dlmk2.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result: ⭐⭐⭐⭐ Surprisingly philosophical&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The table disappeared. The napkin disappeared. The olive dish disappeared.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Only the glass remained.&lt;/strong&gt; The thin stem survived. The delicate base survived.&lt;/p&gt;

&lt;p&gt;And here's the most interesting part: &lt;strong&gt;the refracted image of the flower garden, seen through the glass, was preserved as part of the glass itself.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This might be a philosophically correct answer. The glass is made of glass, but the strongest proof of glass's existence is the distortion of what lies beyond it. The AI kept that distortion as an "attribute of the glass."&lt;/p&gt;

&lt;p&gt;Of course, this means that if you composite this result onto another background, the flower garden will be trapped inside the glass for eternity. A miniature garden in a wine glass. Poetic, but not ideal for product photography.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lv.7 — Playing Dirty
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Challenge: Multiple layers, the definition of "subject" is unstable&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nano Banana 2 prompt:&lt;br&gt;
&lt;code&gt;A person standing behind a rain-covered glass window, their reflection partially visible, city lights blurred in the background, moody cinematic photography, shallow depth of field&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Is the subject the person beyond the window? The window itself? The reflection on the glass? Three layers overlap. Even a human would spend 30 minutes in Photoshop on this one.&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%2F20rmwdgxwtstqt72k55j.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%2F20rmwdgxwtstqt72k55j.png" alt=" " width="800" height="436"&gt;&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%2Flopvlretgrunrmihjeiv.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%2Flopvlretgrunrmihjeiv.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result: ⭐⭐⭐ A Diplomatic Answer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The actual person (right side of the frame) is solidly preserved. The window frame is gone. The raindrops are gone. The blurred city lights are gone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But the reflection lingers.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On the left side of the frame, a faint ghost — the reflection that was on the window — remains, hovering translucently.&lt;/p&gt;

&lt;p&gt;Should the reflection be removed or kept? There is no correct answer. It's similar to asking whether a shadow should be removed.&lt;/p&gt;

&lt;p&gt;The AI appears to have thought: "Removing it entirely doesn't feel right, but keeping it entirely doesn't feel right either." It arrived at the diplomatic solution of &lt;strong&gt;leaving it semi-transparent&lt;/strong&gt; — like a joint statement at an international summit. No one is fully satisfied. No one can fully object.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lv.8 — Collapse
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Challenge: Subject and background are physically fused&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nano Banana 2 prompt:&lt;br&gt;
&lt;code&gt;A ghostly human silhouette barely visible, made entirely of thin wispy smoke, dissolving into a dense dark fog that fills the entire scene, no clear boundary between body and surrounding mist, very low contrast, volumetric fog, dark art photography&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The subject is made of smoke. The background is also smoke. The boundary between subject and background literally does not exist.&lt;/p&gt;

&lt;p&gt;This isn't background removal. This is asking the AI to cut out nothingness.&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%2F0zqo17cvkyftjg3g6jpp.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%2F0zqo17cvkyftjg3g6jpp.png" alt=" " width="800" height="436"&gt;&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%2Fqb9z8q3gg4v3qhgeqvfs.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%2Fqb9z8q3gg4v3qhgeqvfs.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result: ⭐ — Thank you for your service&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The AI cut out something.&lt;/p&gt;

&lt;p&gt;In the center of the frame floats a vague gray smudge. You could say it resembles a human silhouette. You could also say it resembles a ghost photograph. If someone told you it was a new entry in the Rorschach test catalog, that would be the most convincing explanation.&lt;/p&gt;

&lt;p&gt;The dark background fog was removed. But what remains has no presence as a "subject." Smoke was cut from smoke, and smoke is what remained. Logically sound. Practically meaningless.&lt;/p&gt;

&lt;p&gt;This is where the AI draws the line. &lt;strong&gt;When the boundary between subject and background does not physically exist, it cannot be processed.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Which is fair. Asking AI to do what humans can't is unreasonable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Overall Results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lv&lt;/th&gt;
&lt;th&gt;Challenge&lt;/th&gt;
&lt;th&gt;Rating&lt;/th&gt;
&lt;th&gt;One-liner&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;White bg × Apple&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐⭐&lt;/td&gt;
&lt;td&gt;Retirement avoided&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Gradient × Person&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐⭐&lt;/td&gt;
&lt;td&gt;Even the necklace chain survived&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Cluttered desk × Cat&lt;/td&gt;
&lt;td&gt;⭐⭐⭐&lt;/td&gt;
&lt;td&gt;The mug is apparently the cat's ally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;White cat × White sofa&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐&lt;/td&gt;
&lt;td&gt;Rescued the blanket to save the cat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Curly hair × Backlight&lt;/td&gt;
&lt;td&gt;⭐⭐⭐&lt;/td&gt;
&lt;td&gt;To keep the sunlight or not, that is the question&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Wine glass × Garden&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐&lt;/td&gt;
&lt;td&gt;An eternal garden inside the glass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Rain window × Reflection&lt;/td&gt;
&lt;td&gt;⭐⭐⭐&lt;/td&gt;
&lt;td&gt;A joint statement from the UN of pixels&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Smoke person × Smoke bg&lt;/td&gt;
&lt;td&gt;⭐&lt;/td&gt;
&lt;td&gt;New Rorschach test entry&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Lv.1–2: flawless. Lv.3–4: usable. Lv.5–7: depends on context. Lv.8: impossible.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's the state of browser-based AI background removal in 2026.&lt;/p&gt;

&lt;p&gt;Honestly, Lv.4 and Lv.6 exceeded my expectations. I didn't think separating a white cat from a white blanket, or extracting a transparent wine glass, would work this well. Lv.3's decision to keep the books and mug was unexpected, but "rescue all foreground objects together" is actually a smart strategy.&lt;/p&gt;

&lt;p&gt;On the other hand, the light halo in Lv.5 and the reflection ghost in Lv.7 are genuine limitations right now. When pixels sit in an optical gray zone, the AI leans toward "keep it." It prefers the risk of keeping something extra over the risk of destroying the subject. Conservative, but rational.&lt;/p&gt;

&lt;p&gt;And Lv.8 — that's not a background removal problem. That's a "what is a subject" problem. When boundaries don't physically exist, neither AI nor humans can find them.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;So, is it actually usable?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;E-commerce product photos, social media icons, presentation materials — for everyday use cases, Lv.1–4 has you covered. The answer is &lt;strong&gt;yes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And above all, &lt;strong&gt;it runs entirely in your browser.&lt;/strong&gt; No server uploads. Your images never leave your computer.&lt;/p&gt;

&lt;p&gt;For anyone who cares about privacy, that might matter more than performance.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Drop an image in. That's it. Everything happens in your browser.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Curious where your images fall on the scale? Give it a try.&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%2F2dugrnug80mcze331h37.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%2F2dugrnug80mcze331h37.png" alt=" " width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Tools Used
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Image generation: &lt;a href="https://www.nano-banana.ai/" rel="noopener noreferrer"&gt;Nano Banana 2&lt;/a&gt; (Google Gemini family image generation model)&lt;/li&gt;
&lt;li&gt;Background removal: &lt;a href="https://www.imagetools-free.com/bg-remove" rel="noopener noreferrer"&gt;Image Tools&lt;/a&gt; (browser-based, free)&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>ai</category>
      <category>image</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Can AI Ads Cause an International Incident? I Tested NanoBanana2 in 7 Languages to Find Out</title>
      <dc:creator>popo suke</dc:creator>
      <pubDate>Tue, 03 Mar 2026 03:58:59 +0000</pubDate>
      <link>https://dev.to/ogataquin/can-ai-ads-cause-an-international-incident-i-tested-nanobanana2-in-7-languages-to-find-out-8oo</link>
      <guid>https://dev.to/ogataquin/can-ai-ads-cause-an-international-incident-i-tested-nanobanana2-in-7-languages-to-find-out-8oo</guid>
      <description>&lt;h1&gt;
  
  
  Can AI Ads Cause an International Incident? I Tested NanoBanana2 in 7 Languages to Find Out
&lt;/h1&gt;

&lt;p&gt;Tags: &lt;code&gt;ai&lt;/code&gt; &lt;code&gt;imagegeneration&lt;/code&gt; &lt;code&gt;multilingual&lt;/code&gt; &lt;code&gt;google&lt;/code&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%2Fxom87bs6evba1v2l9krc.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%2Fxom87bs6evba1v2l9krc.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;"NanoBanana2 can finally render Japanese text perfectly!"&lt;/p&gt;

&lt;p&gt;Yes. I know. Everyone knows. There are already 200 articles about it.&lt;/p&gt;

&lt;p&gt;Here's what I actually want to know: &lt;strong&gt;Can it write Arabic — right to left — on a souvenir pamphlet without causing an international incident?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm a software engineer living in Vietnam. When you travel around Southeast Asia, you see souvenir shop pamphlets with hilariously wrong foreign text all the time. Every time I see one, I think: "Who approved this?"&lt;/p&gt;

&lt;p&gt;So I asked NanoBanana2 to generate &lt;strong&gt;souvenir pamphlets for 7 countries&lt;/strong&gt;, each in its native language. Same prompt structure. One shot. No retries. No cherry-picking.&lt;/p&gt;

&lt;p&gt;The results? Some pamphlets were ready to hand out at the airport. Others... well, keep reading.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 7 Languages
&lt;/h2&gt;

&lt;p&gt;I deliberately chose languages with the most difficult writing systems. Latin-alphabet languages like English or Indonesian are too easy.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;Script Challenge&lt;/th&gt;
&lt;th&gt;Why I Chose It&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;🇯🇵 Japanese&lt;/td&gt;
&lt;td&gt;3 scripts mixed (Kanji + Hiragana + Katakana)&lt;/td&gt;
&lt;td&gt;Baseline test&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;🇨🇳 Chinese&lt;/td&gt;
&lt;td&gt;High-stroke-count characters&lt;/td&gt;
&lt;td&gt;Can it write "龍" (dragon, 18 strokes)?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;🇰🇷 Korean&lt;/td&gt;
&lt;td&gt;Hangul block composition&lt;/td&gt;
&lt;td&gt;Looks simple, but the rules are strict&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;🇻🇳 Vietnamese&lt;/td&gt;
&lt;td&gt;Diacritical marks everywhere&lt;/td&gt;
&lt;td&gt;I live here. I'm judging this one personally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;🇹🇭 Thai&lt;/td&gt;
&lt;td&gt;3-story stacking (consonant + vowel + tone mark)&lt;/td&gt;
&lt;td&gt;Characters built like apartment buildings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;🇮🇳 Hindi&lt;/td&gt;
&lt;td&gt;Devanagari with connected headline + conjunct characters&lt;/td&gt;
&lt;td&gt;The horizontal line connecting everything&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;🇸🇦 Arabic&lt;/td&gt;
&lt;td&gt;Right-to-left + characters change shape by position&lt;/td&gt;
&lt;td&gt;The final boss&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Combined speakers: &lt;strong&gt;~3 billion people&lt;/strong&gt;. 40% of the world's population.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rules
&lt;/h2&gt;

&lt;p&gt;Fair test. No tricks. This is science. Souvenir pamphlet science.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Same prompt structure for all languages&lt;/strong&gt; — "Generate a souvenir pamphlet that would be posted at a souvenir shop in [country] for [product]"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI writes all the text&lt;/strong&gt; — I didn't specify any text. Product names, catchphrases, descriptions, prices — all AI-generated. If I specified the text, it would just be a copy test, not a language test&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One shot&lt;/strong&gt; — No regeneration. What you see is the first and only attempt&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3 scoring criteria&lt;/strong&gt; — Text accuracy / Design quality / "Would you actually hand this out?"&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  One Thing I Noticed Across All 7
&lt;/h2&gt;

&lt;p&gt;Every single image includes &lt;strong&gt;the souvenir shop in the background&lt;/strong&gt;. I asked for "a pamphlet" and got "a pamphlet elegantly displayed inside a fully rendered souvenir shop." Thanks, I guess.&lt;/p&gt;

&lt;p&gt;Adding "white background, pamphlet image only" to the prompt would fix this. But the shop backgrounds are actually charming, so I'm keeping them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 1: 🇯🇵 Japanese — Matcha KitKat
&lt;/h2&gt;

&lt;p&gt;Warm-up round. If it can't do Japanese, this article is 3 lines long.&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%2Flfm2nl8dey0j04c1ds1d.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%2Flfm2nl8dey0j04c1ds1d.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It did it. Better than expected.&lt;/p&gt;

&lt;p&gt;Natural Japanese headlines, accurate product descriptions, and — this is the part that got me — "個包装10枚入り、配りやすく好評です" (individually wrapped, 10 pieces, popular for sharing). The AI considered the &lt;strong&gt;shareability&lt;/strong&gt; of the souvenir.&lt;/p&gt;

&lt;p&gt;Price: "¥1,200 (税込1,296円)". It calculated the 8% consumption tax correctly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nitpick:&lt;/strong&gt; It reproduced the KitKat logo, which is a trademark gray area. And the Kyoto souvenir shop backdrop is too clean. Real ones are messier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Score: 14/15&lt;/strong&gt; 🟢 Ready to distribute&lt;/p&gt;




&lt;h2&gt;
  
  
  Stage 2: 🇨🇳 Chinese — West Lake Dragon Well Tea
&lt;/h2&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%2Fbackmy0dx6t6v57csraw.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%2Fbackmy0dx6t6v57csraw.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The headline "一口西湖水，满盏龙井香" is a poetic couplet. This doesn't read like AI-generated text.&lt;/p&gt;

&lt;p&gt;The small print accurately describes Dragon Well tea's famous "四绝" (Four Excellences: green color, rich aroma, sweet taste, beautiful shape) — real product knowledge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nitpick:&lt;/strong&gt; The address is fake. Phone number is "XXXXXXXXX". Also, it added "买二送一" (buy 2 get 1 free) without being asked. The AI started running its own promotional campaign.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Add "don't include addresses or phone numbers" to the prompt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Score: 13/15&lt;/strong&gt; 🟡 Remove the fake address, then it's ready&lt;/p&gt;




&lt;h2&gt;
  
  
  Stage 3: 🇰🇷 Korean — Korean Seaweed Gift Set
&lt;/h2&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%2Fns1ulkkcuqst84uxj8sv.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%2Fns1ulkkcuqst84uxj8sv.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hangul looks simple — circles and lines. But the way those circles and lines combine follows strict rules.&lt;/p&gt;

&lt;p&gt;"소중한 분께 전하는 한국의 맛" (Delivering the taste of Korea to someone special). Perfect souvenir headline.&lt;/p&gt;

&lt;p&gt;The small text is grammatically correct Korean. Package labels say "맛김" (seasoned seaweed) and "재래김" (traditional seaweed) — realistic details.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nitpick:&lt;/strong&gt; ₩35,000 (about $25) for a seaweed gift set? That's a bit steep. Also, the AI added another unsolicited promotion: "Buy 3+ sets, get free seaweed flakes." AI, stop trying to upsell.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Specify a price range in the prompt if needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Score: 13/15&lt;/strong&gt; 🟡 Adjust the price, then good to go&lt;/p&gt;




&lt;h2&gt;
  
  
  Stage 4: 🇻🇳 Vietnamese — Vietnamese Coffee
&lt;/h2&gt;

&lt;p&gt;I've lived in Vietnam for 6 years. This is where I judge with near-native eyes.&lt;/p&gt;

&lt;p&gt;Vietnamese has diacritical marks (dấu) on top of diacritical marks. The letter "a" alone has &lt;strong&gt;17 variations&lt;/strong&gt;: à á ả ã ạ â ầ ấ ẩ ẫ ậ ă ằ ắ ẳ ẵ ặ. Can the AI tell them apart?&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%2F5f34dszwnplaneauw4ib.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%2F5f34dszwnplaneauw4ib.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yes. It can.&lt;/p&gt;

&lt;p&gt;It even generated 3 products in columns: "CÀ PHÊ TRỨNG HÀ NỘI" (Hanoi Egg Coffee), "SET PHIN CÀ PHÊ LƯU NIỆM" (Souvenir Phin Coffee Set), "CÀ PHÊ GÓI DẠNG PHIN GIẤY" (Drip Bag Coffee). These are the actual top 3 coffee souvenirs tourists buy in Vietnam. The product selection is too accurate.&lt;/p&gt;

&lt;p&gt;I checked the diacritical marks character by character. &lt;code&gt;ò&lt;/code&gt;, &lt;code&gt;ệ&lt;/code&gt;, &lt;code&gt;ữ&lt;/code&gt;, &lt;code&gt;ủ&lt;/code&gt;, &lt;code&gt;ắ&lt;/code&gt; — all correct positions, correct marks.&lt;/p&gt;

&lt;p&gt;The background shows "QUÀ LƯU NIỆM HỘI AN" (Hoi An Souvenir Shop) with lanterns. If you've been to Hoi An, you'd recognize this vibe instantly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nitpick:&lt;/strong&gt; 150,000 VND (~$6) for an egg coffee gift is a bit cheap for a tourist area. And 3 products on one pamphlet is a lot of information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; "Focus on a single product with a simpler design" would make it cleaner.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Score: 14/15&lt;/strong&gt; 🟢 Approved by a Vietnam resident&lt;/p&gt;




&lt;h2&gt;
  
  
  Stage 5: 🇹🇭 Thai — Mango Sticky Rice
&lt;/h2&gt;

&lt;p&gt;Thai script is a 3-story building. Consonants on the first floor, vowels on the second, tone marks on the third. Change the first floor and the upper floors shift.&lt;/p&gt;

&lt;p&gt;I honestly expected things to start falling apart here.&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%2Fh5erx672j0bd8z9iiduq.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%2Fh5erx672j0bd8z9iiduq.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They didn't fall apart.&lt;/p&gt;

&lt;p&gt;"สุดยอดของฝากจากเมืองไทย" (Thailand's Best Souvenir) — the headline Thai script looks accurate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Small text:&lt;/strong&gt; This is where the 7-language test showed the most variance. When the font size gets smaller, the distinction between vowel marks and tone marks becomes blurry. The text isn't "broken" — it's "hard to read."&lt;/p&gt;

&lt;p&gt;Thai script's stacking nature means small font sizes are particularly dangerous. This might be a fundamental limitation rather than a NanoBanana2 problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nitpick:&lt;/strong&gt; ฿280 (~$8) for mango sticky rice souvenir is actually reasonable. Credit where it's due — AI has good price sense.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; &lt;strong&gt;"Use larger font sizes for all text"&lt;/strong&gt; — this one instruction could fix the readability issue. Especially critical for Thai.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Score: 11/15&lt;/strong&gt; 🟡 Increase font size, then usable&lt;/p&gt;




&lt;h2&gt;
  
  
  Stage 6: 🇮🇳 Hindi — Masala Chai Gift Set
&lt;/h2&gt;

&lt;p&gt;Devanagari script. A horizontal line (shirorekha) runs across the top of each word, connecting all the letters. When consonants cluster, they merge into "conjunct characters" — two letters fusing into an entirely different shape. Even humans mess this up.&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%2Fck98gox9iaowyhto2dp9.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%2Fck98gox9iaowyhto2dp9.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This was the biggest surprise of the entire test.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"शाही मसाला चाय उपहार सेट" (Royal Masala Chai Gift Set). The shirorekha connects correctly.&lt;/p&gt;

&lt;p&gt;Small text lists ingredients with terrifying accuracy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"शुद्ध असम चाय की पत्तियां (१०० ग्राम)" (Pure Assam tea leaves, 100g)&lt;/li&gt;
&lt;li&gt;"६ प्राकृतिक मसाले (इलायची, अदरक, दालचीनी, लौंग, काली मिर्च)" (6 natural spices: cardamom, ginger, cinnamon, clove, black pepper)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It listed 6 spices &lt;strong&gt;by their individual Hindi names&lt;/strong&gt;. And the numbers use Devanagari numerals (१, २, ६) instead of Arabic numerals — the correct choice for Hindi-language content.&lt;/p&gt;

&lt;p&gt;Conjunct characters like "प्रा", "स्वा", "ग्रा" are all rendered in their correct combined forms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nitpick:&lt;/strong&gt; The shop name "सोनी गिफ्ट हाउस" (Sony Gift House) might remind people of a certain electronics company. And Delhi's Main Bazaar is definitely not this photogenic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Score: 14/15&lt;/strong&gt; 🟢 The Devanagari accuracy is genuinely impressive&lt;/p&gt;




&lt;h2&gt;
  
  
  Stage 7: 🇸🇦 Arabic — Dates (Najma Palm Fruit) [FINAL BOSS]
&lt;/h2&gt;

&lt;p&gt;Final stage.&lt;/p&gt;

&lt;p&gt;Arabic is written right to left. The entire layout reverses. Letters never stand alone — they change shape based on their position (beginning, middle, end of a word). Same letter, four different forms.&lt;/p&gt;

&lt;p&gt;Arabic speakers also have extremely high standards for text aesthetics. There's a thousand-year calligraphy tradition. "Sort of readable" doesn't cut it.&lt;/p&gt;

&lt;p&gt;Let me be honest: if this one falls apart, nobody would blame the AI.&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%2F7revtj8ed8kf54mtyms7.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%2F7revtj8ed8kf54mtyms7.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It didn't fall apart. &lt;strong&gt;The final boss has been defeated.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"هدايا تمور فاخرة من المملكة" (Luxury Date Gifts from the Kingdom). RTL is completely correct.&lt;/p&gt;

&lt;p&gt;It generated 4 products in columns arranged &lt;strong&gt;right to left&lt;/strong&gt;. Even the column order follows RTL. I did not expect this level of RTL commitment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Small text:&lt;/strong&gt; The ligatures (letter connections) look natural. Arabic characters change form based on position (initial, medial, final, isolated), and from what I can see, the forms are correct. However, I'll be honest — I can't read Arabic. The shapes look right, but I can't be 100% certain about the grammar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nitpick:&lt;/strong&gt; This needs a native Arabic speaker to do a final check. My "looks correct" judgment is worth exactly as much as a non-Arabic-speaker's opinion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; For any language you can't read, always get a native check. But the structure, direction, and design are all pointing the right way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Score: 13/15&lt;/strong&gt; 🟡 Get a native check, then it's ready. The boss is defeated&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Scoreboard
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Country&lt;/th&gt;
&lt;th&gt;Souvenir&lt;/th&gt;
&lt;th&gt;Text&lt;/th&gt;
&lt;th&gt;Design&lt;/th&gt;
&lt;th&gt;Usable?&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🇯🇵 Japan&lt;/td&gt;
&lt;td&gt;Matcha KitKat&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;14/15&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🟢 Ship it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🇨🇳 China&lt;/td&gt;
&lt;td&gt;Dragon Well Tea&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;13/15&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🟡 Remove fake address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🇰🇷 Korea&lt;/td&gt;
&lt;td&gt;Seaweed Set&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;13/15&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🟡 Price too high&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🇻🇳 Vietnam&lt;/td&gt;
&lt;td&gt;Coffee&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;14/15&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🟢 Resident approved&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🇹🇭 Thailand&lt;/td&gt;
&lt;td&gt;Mango Rice&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;11/15&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🟡 Make text bigger&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🇮🇳 India&lt;/td&gt;
&lt;td&gt;Masala Chai&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;14/15&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🟢 Best surprise&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🇸🇦 Saudi&lt;/td&gt;
&lt;td&gt;Dates&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;13/15&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🟡 Need native check&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Headlines: Perfect Across All 7
&lt;/h3&gt;

&lt;p&gt;Every language produced accurate, natural-sounding headlines. Kanji, Hangul, diacritical marks, Devanagari, Arabic script — nothing collapsed. This alone is a massive improvement from where AI image generation was a year ago.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Small Text: That's Where the Differences Show
&lt;/h3&gt;

&lt;p&gt;Japanese, Vietnamese, and Hindi stayed accurate even in small print. Thai struggled with readability when font size decreased due to its stacking nature. &lt;strong&gt;Adding "use larger font sizes" to the prompt would likely fix this.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The AI Understands Commerce
&lt;/h3&gt;

&lt;p&gt;Without being asked, it generated "buy 2 get 1 free" promotions, loyalty discounts, tax-inclusive pricing, and package deals. NanoBanana2 didn't just generate text — it generated &lt;strong&gt;sales strategy&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Quick Prompt Fixes for Common Issues
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Add to Prompt&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Background shop showing&lt;/td&gt;
&lt;td&gt;"White background, pamphlet image only"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Small text readability&lt;/td&gt;
&lt;td&gt;"Use larger font sizes for all text"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fake addresses&lt;/td&gt;
&lt;td&gt;"Don't include addresses or phone numbers"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Too much info&lt;/td&gt;
&lt;td&gt;"Focus on a single product, simple design"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Bonus: The Header Image Was Also Made by NanoBanana2
&lt;/h2&gt;

&lt;p&gt;I asked it to generate "a UN conference investigating AI-generated advertisements."&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%2Fh8q7n8lwteqpkkcnvfrc.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%2Fh8q7n8lwteqpkkcnvfrc.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pretty good, right? The conference hall atmosphere, colorful pamphlets scattered across the table, "AI-GENERATED ADVERTISEMENT INVESTIGATION" on the big screen. Serious delegates in suits scrutinizing tourist brochures. Exactly what I asked for.&lt;/p&gt;

&lt;p&gt;But look closely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The person sitting at the "JAPAN" nameplate appears to be Indian. The person at "INDIA" appears to be East Asian.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;NanoBanana2 can write perfect pamphlets in 7 scripts, but it still can't match country nameplates to the right people.&lt;/p&gt;

&lt;p&gt;...So it almost caused an international incident after all.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I asked NanoBanana2 to make souvenir pamphlets in 7 of the world's hardest scripts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No international incidents occurred.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I went into this fully expecting at least one "💀 would cause complaints if distributed" verdict. The lowest score was 🟡 (usable with minor fixes). Even Arabic — the final boss with its right-to-left layout and position-dependent letter shapes — was handled correctly.&lt;/p&gt;

&lt;p&gt;Three things genuinely surprised me:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Hindi&lt;/strong&gt; — Conjunct characters, connected shirorekha, Devanagari numerals. All correct. The biggest "I did not see that coming" of the test&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vietnamese&lt;/strong&gt; — I judged this with 6 years of residency. 17 variations of "a" and it nailed them all&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The universal sales instinct&lt;/strong&gt; — The AI doesn't just write text. It writes marketing copy, calculates taxes, and invents promotions. It learned to sell&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One thing I know for certain:&lt;/p&gt;

&lt;p&gt;When NanoBanana3 comes out, I'll make 7 more pamphlets. With harder languages. Georgian. Tibetan. Khmer.&lt;/p&gt;

&lt;p&gt;Until then, I'll be in Vietnam, drinking coffee and waiting.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;All tests were conducted using NanoBanana2 (Gemini 3.1 Flash Image) in March 2026.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Every image shown is the first generation — no retries, no cherry-picking.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;I have not performed native speaker verification for any language except Vietnamese. If you spot errors in your native language, please let me know in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>I Built 18 Browser-Based Tools with Zero Server Calls — Here's How</title>
      <dc:creator>popo suke</dc:creator>
      <pubDate>Mon, 02 Mar 2026 02:06:13 +0000</pubDate>
      <link>https://dev.to/ogataquin/i-built-18-browser-based-tools-with-zero-server-calls-heres-how-2pl9</link>
      <guid>https://dev.to/ogataquin/i-built-18-browser-based-tools-with-zero-server-calls-heres-how-2pl9</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Every time you upload a PDF or image to an online tool, you're trusting a random server with your files. What if the tool could run entirely in your browser instead?&lt;/p&gt;

&lt;p&gt;I built two sites that do exactly that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.pdftools-free.com/" rel="noopener noreferrer"&gt;PDF Tools&lt;/a&gt;&lt;/strong&gt; — 8 PDF tools (split, merge, compress, sign, and more)&lt;/li&gt;
&lt;/ul&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%2Fkjt12kqphn90s2bpclqq.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%2Fkjt12kqphn90s2bpclqq.png" alt=" " width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.imagetools-free.com/" rel="noopener noreferrer"&gt;Image Tools&lt;/a&gt;&lt;/strong&gt; — 10 image tools (compress, resize, crop, AI background removal, and more)&lt;/li&gt;
&lt;/ul&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%2Fto5ayhve4zgbhmpr1r0r.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%2Fto5ayhve4zgbhmpr1r0r.png" alt=" " width="800" height="361"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Zero file uploads. Everything runs in JavaScript on your device.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;React 19 + Vite 7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;PDF side: &lt;code&gt;pdf-lib&lt;/code&gt;, &lt;code&gt;pdfjs-dist&lt;/code&gt;, &lt;code&gt;ExcelJS&lt;/code&gt;, &lt;code&gt;jsPDF&lt;/code&gt;&lt;br&gt;
Image side: &lt;code&gt;Canvas API&lt;/code&gt;, &lt;code&gt;@imgly/background-removal&lt;/code&gt;, &lt;code&gt;TensorFlow.js + ESRGAN&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Code Splitting: 2,775KB → 248KB Initial Load
&lt;/h2&gt;

&lt;p&gt;The first build was huge. Every library loaded upfront, even if the user only needed one tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix: React.lazy() + Vite manualChunks&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Each tool loads only when selected&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SplitMode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lazy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./components/SplitMode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MergeMode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lazy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./components/MergeMode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CompressMode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lazy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./components/CompressMode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// vite.config.js — separate heavy libs into their own chunks&lt;/span&gt;
&lt;span class="nx"&gt;manualChunks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bg-removal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@imgly/background-removal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;// ONNX Runtime&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tfjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@tensorflow/tfjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;                  &lt;span class="c1"&gt;// ~2MB&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;upscaler&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;upscaler&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@upscalerjs/esrgan-medium&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pdfjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pdfjs-dist&lt;/span&gt;&lt;span class="dl"&gt;'&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;Result: &lt;strong&gt;91% reduction&lt;/strong&gt; in initial load. Users only download what they use.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Canvas API Does More Than You Think
&lt;/h2&gt;

&lt;p&gt;Most of Image Tools runs on Canvas API alone — no image processing libraries needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compression&lt;/strong&gt; — just &lt;code&gt;toBlob()&lt;/code&gt; with quality parameter:&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="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBlob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// compressed blob ready&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/jpeg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 70% quality&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Free-angle rotation&lt;/strong&gt; — the tricky part is calculating the expanded canvas size:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rad&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rad&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rad&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Canvas must be large enough to fit the rotated image&lt;/span&gt;
&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;cos&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;canvas&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="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;sin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&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="nx"&gt;canvas&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="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rad&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;w&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="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;h&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;CSS Filters baked into images&lt;/strong&gt; — apply filters and export as a file:&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="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`brightness(120%) contrast(110%) saturate(130%) grayscale(50%)`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Now export — filters are permanently applied to the pixels&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Custom Binary Encoders for BMP, GIF, and ICO
&lt;/h2&gt;

&lt;p&gt;Canvas &lt;code&gt;toBlob()&lt;/code&gt; supports JPEG, PNG, WebP, and AVIF. But not BMP, GIF, or ICO.&lt;/p&gt;

&lt;p&gt;I wrote custom encoders from scratch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BMP&lt;/strong&gt; — requires BGR byte order and bottom-up pixel storage:&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="c1"&gt;// RGBA from Canvas → BGR for BMP (plus row padding to 4-byte boundary)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rowSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&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;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;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;y&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// bottom-up&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&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;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;src&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="c1"&gt;// B&lt;/span&gt;
    &lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dst&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// G&lt;/span&gt;
    &lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dst&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="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;     &lt;span class="c1"&gt;// R&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;strong&gt;GIF&lt;/strong&gt; — includes LZW compression and 256-color quantization:&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="c1"&gt;// 1. Quantize colors to fit 256-color palette&lt;/span&gt;
&lt;span class="c1"&gt;// 2. Build color lookup table&lt;/span&gt;
&lt;span class="c1"&gt;// 3. LZW encode with dictionary reset at 4096 entries&lt;/span&gt;
&lt;span class="c1"&gt;// 4. Pack into GIF89a format&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Writing a GIF encoder was the most challenging part — LZW compression with variable-width codes and dictionary management is not trivial.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. AI in the Browser — No Server Needed
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Background removal&lt;/strong&gt; with ONNX Runtime (~50MB model, cached after first use):&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;removeBackground&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@imgly/background-removal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;removeBackground&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setProgress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// Returns transparent PNG&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fz5m89norxkq5zupzs8ay.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%2Fz5m89norxkq5zupzs8ay.png" alt=" " width="800" height="412"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;AI upscaling&lt;/strong&gt; with TensorFlow.js + ESRGAN:&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="kd"&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="nc"&gt;Upscaler&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;upscaler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upscale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;patchSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Process in 64px tiles to avoid GPU OOM&lt;/span&gt;
  &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// Overlap between tiles to prevent seams&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;patchSize: 64&lt;/code&gt; trick is critical — without it, large images crash the browser's GPU memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Detecting AI-Generated Image Metadata
&lt;/h2&gt;

&lt;p&gt;The Metadata tool parses PNG binary chunks to detect AI generation parameters:&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="c1"&gt;// PNG files contain tEXt and iTXt chunks with metadata&lt;/span&gt;
&lt;span class="c1"&gt;// AI tools embed their parameters here:&lt;/span&gt;
&lt;span class="c1"&gt;// - "parameters" → Stable Diffusion (AUTOMATIC1111)&lt;/span&gt;
&lt;span class="c1"&gt;// - "prompt" / "workflow" → ComfyUI&lt;/span&gt;
&lt;span class="c1"&gt;// - "comment" / "source" → NovelAI&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This required writing a custom PNG chunk parser that reads the binary format byte by byte.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Lightweight i18n Without Libraries
&lt;/h2&gt;

&lt;p&gt;Both sites support 5 languages (EN, JA, VI, ID, ZH) with a custom 30-line implementation:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;translations&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;]?.[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;translations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;en&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\{(\d&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)\}&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Auto-detect browser language on first visit&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browserLang&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&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="nx"&gt;browserLang&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ja&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ja&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;browserLang&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No &lt;code&gt;react-i18next&lt;/code&gt;, no &lt;code&gt;formatjs&lt;/code&gt;. Just a plain object lookup with placeholder substitution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost: $1.75/month
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Hosting (Vercel) x2&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Domains (Cloudflare) x2&lt;/td&gt;
&lt;td&gt;~$21/year&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSL&lt;/td&gt;
&lt;td&gt;Free (Cloudflare)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Libraries&lt;/td&gt;
&lt;td&gt;All OSS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI models&lt;/td&gt;
&lt;td&gt;Browser-delivered&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$1.75/month&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Since all processing is client-side, server costs don't scale with users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PDF Tools&lt;/strong&gt;: &lt;a href="https://www.pdftools-free.com/" rel="noopener noreferrer"&gt;pdftools-free.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image Tools&lt;/strong&gt;: &lt;a href="https://www.imagetools-free.com/" rel="noopener noreferrer"&gt;imagetools-free.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub (PDF)&lt;/strong&gt;: &lt;a href="https://github.com/plusishibba-design/pdf-modify" rel="noopener noreferrer"&gt;github.com/plusishibba-design/pdf-modify&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub (Image)&lt;/strong&gt;: &lt;a href="https://github.com/plusishibba-design/image-tools" rel="noopener noreferrer"&gt;github.com/plusishibba-design/image-tools&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're building browser-based tools, I hope some of these techniques are useful. Happy to answer questions!&lt;/p&gt;

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