Floating point to hex converter is now framework free! Here’s what I learned

Here it is! And for comparison, here’s the old version that used React. As intended, it looks the same! (except for the “swap to use big-endian” option, which I don’t think makes a lot of sense)

This is the latest project on my framework free kick – you can read previous entries here. This was the biggest project that I’ve converted from React to Web Components; the diff is here and the main code is the 855 lines of public/index.js.

The floating point to hex converter is the most popular page on my website (just ahead of my MLB Triple Crown tracker, which will start dropping off any day now) so making big changes to it is scary! Especially since I don’t really have any sort of reporting that would let me know if something was broken. I did port over the existing tests to something based on Open Web Components’ testing framework, which lets me actually create components in the DOM, click buttons, etc. But if you see anything wrong, please drop me a line 🙂

One of the fringe benefits of moving away from React was to get improved performance, more out of principle than any real need. But I did think that having a “pure HTML” page instead of pulling in React would make the page smaller and perform better. And, while it’s smaller, the performance results are more mixed: here’s the PageSpeed Insights for the new site compared with the old site. Unfortunately, I’m not an expert here; some things seem better and some seem worse. Oh well!

As this was my biggest Web Components project, and I wrote it without the help of LLMs (with one irritating exception; see below), I learned a lot:

  • HTML attributes have to be lowercase – Web Components generally store their values in attributes, and at once point I was setting an attribute listed in the observedAttributes list, but the call to attributeChangedCallback() wasn’t happening. And you can’t set a breakpoint on a call that isn’t happening, and I couldn’t figure out what could be going wrong, as this was something I’d done in all my previous Web Components projects. I started feverishly reverting changes with commit titles like “flailing about” and “aaa what is happening”. Finally, after being stuck for hours I finally gave in and asked Claude, and while it misdiagnosed the issue it did literally mention “as a sidenote” that HTML attributes are lowercase and if they’re ever set in HTML (as opposed to Javascript) trying to use the mixed-case version would cause problems. And lo and behold, that fixed the problem and I learned a valuable lesson I’ll probably never forget!
  • I still still miss JSX – Not much to add over my previous thoughts, but I felt it even more this time because the components were more complicated and had more attributes. Since components were nested a bit, passing attribute values down was also more irritating. If I work on a more complicated project maybe I will finally look into something like WebJSX to make this easier.
  • React uses className, HTML uses class – I’m impressed at my ability to keep getting this wrong.
  • Elements that get slotted (inserted in a <slot>) still exist in their original DOM position – In a component you can include a <slot> placeholder, which is where child elements of the original component get put. But the HTML structure doesn’t actually change, so if you need to query the DOM to find the child elements they aren’t actually in the <slot>. In retrospect I think this makes some sense, but it confused me for a while!

Questions I didn’t get satisfactory answers to:

  • Is there no way to set multiple attributes at the same time? Since my components have a lot of different attributes, there were a number of places that look like this, where the property setters are setting or removing attributes. Each time that happens the attributeChangedCallback() gets called, which means everything gets updated, so this effectively causes 5 updates to happen, most with intermediate values that are thrown away. This is madness! I thought of two options to fix this, but they’re both pretty bad. Is there something I’m missing?
    • Only call update() when the “last” attribute changes, which means callers would have to know the right order to set those attributes in, which is a terrible API.
    • Have a property that callers can set to pass a bunch of relevant attributes at once, separated by delimiters or something. This is possibly worse. Although I guess I could add a method on the class that does something like this, so callers could just call that method and wouldn’t have to know about the magic property. Hmm, this may be the best option.
  • How does JSDoc work in Visual Studio Code? Since I wanted to just write a plain JS file and avoid a build step, I skipped Typescript and was hoping to use JSDoc annotations to specify types. However, in all but the simplest cases I didn’t see any type errors flagged by Visual Studio Code, which was a bummer. It would be nice to have some sort of type checking while still writing plain JS!
  • Is there a better way to reset an animation when it’s done? To get the satisfying yellow flash when a conversion is done, I use a CSS transition, and then add a class to an element to trigger it. But after the animation is done, I have to remove that class so that the next time I can add it back to trigger the animation, and the only way I could figure out to do that was to parse the CSS to get the animation duration (which a pretty cool thing to be able to do!) and remove the class after that time elapses. Not the worst thing in the world, but it does feel klunky; maybe there’s a better way?

But when all is said and done, it’s really nice to have a plain HTML and JS file that I can build and deploy by hitting “Save” in an editor. (well, OK, and pushing to GitHub. But still!) And it’s similarly nice to have a non-minified JS file that’s easy to inspect. While I’m a bit skeptical about using Web Components for larger projects because of the questions above, the immediate feedback and nice clean files are really satisfying, so I’ll be on the lookout for more projects that I can convert!

Name My Color is now framework free!

TL;DR: I rewrote Name My Color to use Web Components instead of React!

My anti-web framework kick continues; after using Web Components for the baseball triple crown tracker, I’ve been hoping to start rewriting older projects in a more Web-native way. (honestly, at least part of this is to avoid those annoying GitHub dependabot emails warning me about vulnerabilities in my dependencies!)

The project is fairly straightforward (just a few hundred lines of JS), but I didn’t think I could do this because of the color picker. then I remembered seeing there’s an <input type=”color”>, and it’s well supported!

Of course the flip side is that instead of using a React component that looks the same everywhere, you’re depending on how different browsers implement things, so you have to be OK with the fact that your design isn’t going to be pixel perfect. Firefox’s color picker is platform-specific (the Linux one seems especially basic), although a nicer HTML-based one is coming!

An annoyance was reimplementing the feature that to allow clicking on a color to show the hex color (in case you’re on mobile and can’t see the tooltip). The old version did this in a typical React-ish way, where the component has an expanded boolean in its state, and clicking on it toggles that and shows the color. It hit me that this was really just the same as <details> and <summary> elements, and then I wouldn’t have to track the state! Which was true, except I couldn’t find a way to hide the triangle that opens on Safari, as for some reason it doesn’t support the ::marker pseudo element. I tried some solutions that were supposed to work on Safari but I just couldn’t get it to work and eventually gave up and embraced the “not pixel perfect” lifestyle 🙂 Edit: I did figure this out a few days later!

As I’ve been trying to do with my personal projects, I didn’t use any LLMs in doing this…except for a few things. One was figuring out how to import ESM modules from a CDN (it’s weirder than you think!) because I did want to reuse some libraries for doing the color calculations instead of writing JS like this to convert RGB to CIELAB. Another was getting tests working – I sort of followed this guide, but didn’t really want to test any DOM stuff, so I made it a little simpler then got hopelessly lost and ended up with this spaghetti code. But look at the slick tests page!

Honestly I’m not totally sure why I wrote this originally – I think the basic idea was that you could take a picture of something and then get a name for a color? Although I guess this would require the EyeDropper API and also displaying a picture? But regardless it’s kinda fun to play with, and sometimes the kids will play with it for a few minutes, and that’s good enough for me!

And it is awfully satisfying to have a commit with net -28000 lines 🙂

Some people are really upset about LLM watermarking and I don’t get it

This week Anthropic announced that to comply with an EU AI Act they would watermark text generated by Claude models. A few tech bloggers I respect had strong negative reactions to this, and I wanted to walk through their objections and why I disagree.

Disclosure: I use LLMs for coding stuff at work (mostly Claude Code), and I do use a Claude version occasionally for tech questions at home. (mostly related to administering my Linux machine, which I have become increasingly bad at) I haven’t really used LLMs for coding my home projects apart from the occasional question, and I purposefully don’t use them to write.

Daring Fireball

John Gruber of Daring Fireball wrote Anthropic Posts ‘How Claude Marks AI-Generated Content’ Without Explaining How Claude Marks AI-Generated Content. Note that this post was written before Anthropic released more details about how the watermark actually works; basically when Claude makes a choice about what word to use, it will bias toward some statistical property (a very basic example would be “pick the word that has more ‘e’s”) that can later be detected.

So some of the post is talking about invisible characters, which we now know is not how it works. But Gruber also complains that:

  • If it’s doing this biasing then it’s not making optimal choices for what it outputs – I guess this is technically true, but LLMs can run with different temperatures already which means they won’t make the “most likely” choice. I am skeptical that you could realistically tell the difference between a response that is watermarked and one that is not.
  • What if you copy someone’s text and quote it in something you write? Then your writing will show up as Claude-generated! Yes, but a convincing counterargument is “try running the non-quoted parts through the detector”.
  • “They obviously need to explain exactly what they’re embedding in text if anyone is actually going to detect it, but if they explain it, anyone can simply remove it.“ Yup, this is why watermarking text is tricky 🙂 And the secret sauce is what statistical choices Claude biases towards, which will presumably not be released.

Rands in Repose

Rands wrote RIP Claude with the following complaints:

  • There’s no way to opt out – Yes, I think this is kinda the point of watermarking! If you can opt out then what would the point be?
  • Many actions, like summarization and critique, will add watermarks Again, I’m not sure what else one would expect. Maybe only generating text from a prompt could be watermarked, but that seems extremely subjective to me.
  • “My writing is my work, and Anthropic’s current strategy is aggressively writer-hostile.“ Maybe I don’t have the right perspective since I don’t use LLMs for writing, but it seems like Rands wants to be able to use Claude to help with their writing and not have it trigger the Claude detector. I think this is along the lines of this thoughtful Medium post about how there is good writing that uses AI and bad writing that does not use AI. And I guess I can understand if Rands thinks people are going to use the Claude detector alone to determine whether the writing is good or bad. But I think this is still better than the status quo (more below)

My thoughts

Both of these articles seemed much angrier than I would have expected. (they also seem focused specifically at Anthropic instead of the EU law that Anthropic is complying with, which makes me think that OpenAI is going to have to do something similar)

I think it is generally useful to have a tool that can tell you whether words were generated by Claude or not, especially compared with the AI detecting “tools” we have today that are not reliable.

I guess in theory I’m a tiny bit worried that this will be used to stigmatize tiny bits of Claude-written stuff that thoughtful people use sparingly. But I think there’s so much LLM generated slop out there that it will be more useful than harmful.

The Score: How to Stop Playing Somebody Else’s Game (sort of) review

The Score: How to Stop Playing Somebody Else's GameThe Score: How to Stop Playing Somebody Else’s Game by C. Thi Nguyen




(no rating because I didn’t actually finish the book; thanks Goodread’s new Did Not Finish shelf!)

Man, I tried to get into this book, but every time I tried I just could not do it – after a month I wasn’t even up to 30%! The book has interesting ideas but it just seemed so repetitive.

Nguyen’s main idea is that it’s very easy to fall subject to “value capture” – when you start being guided by external metrics instead of your own, like a restaurant that stops caring about making good food and starts caring about maximizing its ratings. “Making good food” is a hard thing to measure, and “average rating” is much much easier, so over time it’s easy to focus on the latter and deemphasize the former.

Another interesting bit was about games. We like to play games, but most of the time winning or losing doesn’t matter that much to us. But in most cases you have to try to win to enjoy the game; apparently this is called a “self-effacing end”, which is an end (in this case, enjoyment) that can’t be pursued directly.


View all my reviews