Project in Detail: Fuse Bead Color Matcher

Submitted by Josh on

My daughter's winter break got kind of blown up in 2022 due to some really broken weather patterns and Southwest Airlines' inability to cope with them, so we spent a lot more time at home than we expected. As it sometimes does, that resulted in a couple projects leveraging our big stash of Perler Beads, both in terms of making new designs and in sorting out some of our myriad colors that got mixed together.

 
I started using Perler Beads when I was about the same age as my daughter is now, and like a lot of people of my generation, one of the things I did the most growing up (and now!) are beaded versions of pixelated sprites from video games; it stands to reason, really, because the visual connection between a pixel on a screen and a single bead is completely clear. Some artists more talented than I produce absolutely insane designs from video game art, and I myself once made a sprite about two feet tall (but that one was a four-color job and not all that intricate). Evidence presented here in the form of a truly amazing piece of work from an artist on Reddit; there's a reason I didn't put anything I've created in that space.
 
When we were building things recently, I tried to pull a sprite from a book, only to discover that I didn't have very good color matches. It wasn't a design that I was extremely enthusiastic about making, so I let it drop without being completed, but it made me think about what I would have done if it was something I was really excited to build and I didn't have the colors I wanted, and I realized that a tool to do color matching would be ideal. tl;dr - you can find the tool here.

Specifications

I decided to put together a relatively simple tool for the web because that's what I do, and because I had a couple newer technologies I wanted to try out. So, I decided to put together a webpage that could allow the user to select colors from a color picker, and have the site determine the closest matches based upon a finite set of bead colors that are sold to the public, and to display those matches in a hierarchy so the user would have multiple options.
 
I decided to do the project using NextJS with Typescript and TailwindCSS, as both are frameworks that are growing in popularity and with which I hadn't had much experience. I determined early on that I did not want to have a living NextJS app to host, though, so I further determined that I would build the static pages from Next and host them as simple HTML. I also determined that I wanted my data to be powered by a flat JSON file because I wanted something extremely simple to work with and had no interest in building an API layer to serve data that is pretty static in nature.
 
I decided to call this little app "fusecolors," as that's just what it is: a bunch of colors of heat-fused beads that you can access on-demand.
 
Here is a cleaned-up version of the features that I put together before starting development:
  1. A color picker capable of selecting from the standard RGB gamut either by point-and-click or manual input. Supporting various formats for input, particularly hexadecimal, would be ideal.
  2. An internal list of all commercially available colors from Perler to use for comparison.
  3. A methodology to convert sRGB input to LAB color, which I realized after some research is the standardized way of doing direct color comparisons.
  4. A methodology to compare two LAB colors to determine their proximity.
  5. Ranked matching of LAB comparisons to give multiple options.
  6. Linking to products via Amazon Affiliate links.

The App

I first set out to make just a single page application for this, just a page that takes user input, runs the calculations, and renders the output all via javascript. I later decided to add a second page for FAQs that is entirely static and can mostly be ignored for the purposes of this post. For the picker page, I simply needed a picker element and a space to show results.
 
When the app loads, it loads the UX and the Javascript necessary, and also stores into memory the list of control colors from the different brands of beads. I used the values from the Bead Colors reference, a community-compiled list of beads from various manufacturers. I wrote a helper script to grab those values and convert them to LAB, and then generated a static JSON file to store all the data, as there is no need to compute the values at runtime as they will rarely, if ever, change. The app then loads the color picker, necessary for all the other features.
 
I would have probably liked to use a bespoke color picker to work with a custom UX for this, but I'm not getting paid here - so, I grabbed a NextJS component off the shelf that had prebuilt UX to mimic a few popular color pickers in use today. I set it to emulate Google's picker, as it has a nice visual picker as well as the ability to manually input values in several different spaces. The picker I chose had all the necessary events I wanted, as well, so that picking a color in any fashion would fire off an event with the selected value that I could hook to the rest of the flow.
 
That hook is integral to the rest of the functionality; once a color is selected, the rest of the steps happen on the fly. First, the selected color is converted from sRGB to LAB color space by way of the color-convert library, so that we can compare apples-to-apples with the set of control colors. Then, another library, delta-e, actually does the comparison to create a delta between the selected color and each control. There are currently 350 colors in my control data; I originally thought with so many colors that I would have to plan a way to make the comparisons more performant and possibly even store the LAB values of each control color and organize the data pre-render, but the libraries and comparisons load and execute so quickly that there was no need. Results are presented virtually instantaneously.
 
After all the comparisons have been made, the app will organize them by best match, and render out swatches of the top eleven choices to the viewport. Why eleven? I started with just the top three but discovered that it left me guessing if the comparisons might have missed something I'd like to see. I then switched to six, but didn't like the way they laid out because it made it difficult to identify the best colors visually; every match looked at first glance to have equal priority. I switched to a nice round ten and found a similar problem, and finally landed on eleven with a top tier of the three best matches rendered larger than the second tier, two rows of eight. This worked relatively well even when the responsive viewport shifted down to phone size, so it stuck.

The Results

Every color you pick in the tool will bring back eleven results of varying quality. The Delta-E formula explains that ideal matches would have a Delta-E below 1; while I haven't tried all sixteen million colors available to the sRGB gamut, I have rarely seen values below 2. For the record, one of the best matches I've seen so far is a 0.89 for RGB value 142, 142, 169, and even at that value the color is perceptibly different between selection and control. You're not going to get an exact match here very often, even if you buy every bead on the market.
 
Each swatch that gets output will link to Amazon so you can get the colors you need if you don't have them to hand. Yeah, I made them affiliate links, because that seems pretty fair, but the tool isn't very useful if it gives you information that you then have to copy by hand somewhere to actually get the colors you want anyway.
 
The final feature I plugged in late in the game was a method to store your last ten color selections. This uses the localstorage API, so even if you close the browser and come back later from the same device, your last ten picks will be available to see again.

Success?

The app does most of what I'd like it to do, but I wouldn't really consider it that successful, and that's why I haven't really publicized it. For one, I'm not in love with the UX I ended up with. It just doesn't feel polished, and it's not distinctive enough to stand out from similar apps (that I didn't know existed when I first searched for them and found them only later with different queries). Part of this, I think, is because it was my first Tailwind project, and I'm honestly not crazy about the way Tailwind does things - which is probably its own post for the future.
 
Speaking of other similar apps, I found one called Pixel Beads that can take a bitmap image and convert it into a pattern, while also doing the color matches within single brands of beads and counting the number of beads needed to execute the pattern. While it's not perfect, it does some things really well and I think it would make for a lot more interesting of an app to be able to ingest images that way.
 
And, finally, I'm not really part of that beading community online to speak of. I think if I collected more feedback from avid beaders, I'd feel better about what I've built and what else I should think about doing with it. It also needs some improvements to SEO so that more people can find it on their own in the meantime.
 
But, at the end of the day, I got a little practice with something that will help me do my job and might make future projects more fun for me and the kid, so it's still a big success on that score!