<?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: qingwancong</title>
    <description>The latest articles on DEV Community by qingwancong (@congcong).</description>
    <link>https://dev.to/congcong</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4023087%2Ff00d80b4-f79b-4e77-a3cd-e1979ea635a2.png</url>
      <title>DEV Community: qingwancong</title>
      <link>https://dev.to/congcong</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/congcong"/>
    <language>en</language>
    <item>
      <title>Palette quantization notes: reducing colors without making an image muddy</title>
      <dc:creator>qingwancong</dc:creator>
      <pubDate>Thu, 09 Jul 2026 18:10:35 +0000</pubDate>
      <link>https://dev.to/congcong/palette-quantization-notes-reducing-colors-without-making-an-image-muddy-19im</link>
      <guid>https://dev.to/congcong/palette-quantization-notes-reducing-colors-without-making-an-image-muddy-19im</guid>
      <description>&lt;p&gt;I’ve been thinking about a small image-processing problem lately: how to reduce an image to a limited palette without making it look muddy.&lt;/p&gt;

&lt;p&gt;This comes up in a lot of places:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pixel art tools&lt;/li&gt;
&lt;li&gt;printable pattern generators&lt;/li&gt;
&lt;li&gt;low-color previews&lt;/li&gt;
&lt;li&gt;LED matrix displays&lt;/li&gt;
&lt;li&gt;icons and small thumbnails&lt;/li&gt;
&lt;li&gt;craft or grid-based workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The easy version is: pick the nearest color for every pixel.&lt;/p&gt;

&lt;p&gt;The hard version is: keep the important shapes readable after the palette gets much smaller.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nearest color is only the baseline
&lt;/h2&gt;

&lt;p&gt;A simple nearest-color pass usually works like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Take each pixel.&lt;/li&gt;
&lt;li&gt;Compare it with every color in the target palette.&lt;/li&gt;
&lt;li&gt;Pick the closest one.&lt;/li&gt;
&lt;li&gt;Replace the pixel.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That gives you a valid output, but not always a good one.&lt;/p&gt;

&lt;p&gt;The problem is that closest is local. It does not know whether the whole image still reads well.&lt;/p&gt;

&lt;p&gt;A face can lose warm midtones. A shadow can turn into a flat dark blob. A small highlight can disappear. Skin, fur, fabric, and background colors can collapse into the same bucket.&lt;/p&gt;

&lt;p&gt;So palette reduction is not just a color problem. It is also a structure problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  RGB distance can be misleading
&lt;/h2&gt;

&lt;p&gt;A common first attempt is Euclidean distance in RGB:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function rgbDistance(a, b) {
  return Math.sqrt(
    (a.r - b.r) ** 2 +
    (a.g - b.g) ** 2 +
    (a.b - b.b) ** 2
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is easy to implement, but it does not match human perception very well.&lt;/p&gt;

&lt;p&gt;Two colors can be numerically close in RGB and still feel different. Other colors can be farther apart numerically but visually acceptable.&lt;/p&gt;

&lt;p&gt;A better approach is to compare colors in a more perceptual color space, such as Lab or OKLab. You still have to be careful, but the distance metric starts closer to what the eye notices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dithering helps, but it changes the style
&lt;/h2&gt;

&lt;p&gt;Error diffusion, like Floyd-Steinberg dithering, can preserve gradients and perceived detail with fewer colors.&lt;/p&gt;

&lt;p&gt;That is useful when the output is meant to look like a low-color image.&lt;/p&gt;

&lt;p&gt;But dithering is not always desirable. In grid-based outputs, it can create scattered single-pixel noise. That may preserve tone, but it can make the result harder to read or harder to build.&lt;/p&gt;

&lt;p&gt;So the question is not: should I dither?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is texture more important here, or are clean shapes more important?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For a tiny icon, clean shapes often win. For a photograph-like preview, dithering may win.&lt;/p&gt;

&lt;h2&gt;
  
  
  Palette size is a UX control, not just a parameter
&lt;/h2&gt;

&lt;p&gt;A 24-color image and a 6-color image are not just different compression levels. They can feel like different art directions.&lt;/p&gt;

&lt;p&gt;When users control palette size, they are really controlling tradeoffs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;realism vs simplicity&lt;/li&gt;
&lt;li&gt;smoothness vs readability&lt;/li&gt;
&lt;li&gt;detail vs editability&lt;/li&gt;
&lt;li&gt;texture vs clean regions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why I like showing a preview before committing to the palette. A numeric color count alone does not tell the user what the image will feel like.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preserve important regions differently
&lt;/h2&gt;

&lt;p&gt;One useful trick is to treat the whole image less uniformly.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;preserve contrast around eyes and facial features&lt;/li&gt;
&lt;li&gt;simplify background colors more aggressively&lt;/li&gt;
&lt;li&gt;keep outlines cleaner than texture regions&lt;/li&gt;
&lt;li&gt;protect small high-salience details from being averaged away&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This does not require a huge ML pipeline. Even simple masks, edge detection, or manually marked regions can help.&lt;/p&gt;

&lt;p&gt;The main idea: not every part of the image deserves the same color budget.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical pipeline
&lt;/h2&gt;

&lt;p&gt;A basic but workable pipeline might look like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resize image
-&amp;gt; optional subject/background separation
-&amp;gt; smooth tiny noise
-&amp;gt; choose or load target palette
-&amp;gt; map pixels using perceptual distance
-&amp;gt; optionally apply controlled dithering
-&amp;gt; clean isolated pixels
-&amp;gt; preview multiple color counts
-&amp;gt; let the user pick the best tradeoff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The cleanup step matters more than it sounds.&lt;/p&gt;

&lt;p&gt;After quantization, isolated pixels often appear because the algorithm is optimizing locally. A small post-pass that removes lonely pixels or merges tiny regions can make the result feel much more intentional.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I watch for
&lt;/h2&gt;

&lt;p&gt;When reviewing a reduced-palette result, I usually check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do the main shapes still read at a glance?&lt;/li&gt;
&lt;li&gt;Did skin or fur become too gray?&lt;/li&gt;
&lt;li&gt;Did important contrast disappear?&lt;/li&gt;
&lt;li&gt;Are there noisy single pixels everywhere?&lt;/li&gt;
&lt;li&gt;Did the background steal colors from the subject?&lt;/li&gt;
&lt;li&gt;Would a human understand why each color exists?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last question is fuzzy, but useful.&lt;/p&gt;

&lt;p&gt;If a color appears only a few times and does not clarify the image, it may be adding complexity without adding meaning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing thought
&lt;/h2&gt;

&lt;p&gt;Palette quantization looks simple because the first version is easy to code.&lt;/p&gt;

&lt;p&gt;But the useful version is more about choosing what to preserve.&lt;/p&gt;

&lt;p&gt;The algorithm can map colors. The product decision is deciding which details are worth keeping.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>algorithms</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building Toonbead: turning photos into bead art you can actually make</title>
      <dc:creator>qingwancong</dc:creator>
      <pubDate>Thu, 09 Jul 2026 17:49:55 +0000</pubDate>
      <link>https://dev.to/congcong/building-toonbead-turning-photos-into-bead-art-you-can-actually-make-14i1</link>
      <guid>https://dev.to/congcong/building-toonbead-turning-photos-into-bead-art-you-can-actually-make-14i1</guid>
      <description>&lt;p&gt;I’m building a small craft tool called Toonbead.&lt;/p&gt;

&lt;p&gt;The idea is simple: upload a pet, portrait, or gift photo and turn it into a fuse bead pattern that someone can actually make at a craft table.&lt;/p&gt;

&lt;p&gt;The tricky part is that “photo to pixel art” is not the same thing as “photo to bead pattern.”&lt;/p&gt;

&lt;p&gt;A pixelated image can look fine on screen and still be painful to build:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;too many colors&lt;/li&gt;
&lt;li&gt;noisy one-off pixels&lt;/li&gt;
&lt;li&gt;no real bead color matching&lt;/li&gt;
&lt;li&gt;no bead counts&lt;/li&gt;
&lt;li&gt;no board layout&lt;/li&gt;
&lt;li&gt;no printable instructions&lt;/li&gt;
&lt;li&gt;no sense of whether the project is beginner-friendly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So Toonbead is focused on the practical craft constraints, not just the image result.&lt;/p&gt;

&lt;p&gt;The workflow I’m aiming for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Upload a meaningful photo.&lt;/li&gt;
&lt;li&gt;Keep the subject recognizable.&lt;/li&gt;
&lt;li&gt;Convert it into a cleaner bead-friendly design.&lt;/li&gt;
&lt;li&gt;Match it to real bead colors.&lt;/li&gt;
&lt;li&gt;Show size, bead count, color count, and difficulty.&lt;/li&gt;
&lt;li&gt;Export a printable pattern that is ready to build.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I’m starting with pet portraits, personal gifts, and beginner-friendly fuse bead projects because those are the situations where the result needs to feel personal and still be realistic to finish.&lt;/p&gt;

&lt;p&gt;What I’m still thinking through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Should users choose board size first, or difficulty first?&lt;/li&gt;
&lt;li&gt;How many colors is “too many” for a beginner pattern?&lt;/li&gt;
&lt;li&gt;Should the first export be a PDF, an on-screen grid, or both?&lt;/li&gt;
&lt;li&gt;How much editing should happen before the pattern is considered final?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you make fuse bead art, pixel art, printable craft patterns, or tools for creative workflows, I’d love feedback.&lt;/p&gt;

&lt;p&gt;Try the early version here: &lt;a href="https://toonbead.com" rel="noopener noreferrer"&gt;Toonbead&lt;/a&gt;&lt;/p&gt;

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