Microblog - 2025

This page lists microblog entries for the year 2025. For entries from other years, consult the microblog archive.

Converting images to WebP format (part 2)

It turns out I was mistaken! You actually can export .webp files using Affinity Photo. They give you slightly more flexible settings than the Opti-WebP program I was using, so I’m going to switch to Affinity Photo for future images.

I was able to further reduce my homepage’s image to a little below 24 kB using their method, by both increasing pixel count but also decreasing compression fidelity. That means our file size went

1621 kB -> 32 kB -> 24 kB

over time.

I still think it’s worth explaining how Opti-WebP works for a tutorial for people trying to do this same endeavor, since I think it’s unreasonable to assume the average person owns Affinity Photo. I have a license, but very few people do.

Converting images to WebP format

I’ve been trying to convert all of the images on my site to WebP format, while also retaining the original versions of the images for posterity’s sake. I want to make sure that I, the website creator, can always access the archived versions of the images in their non-compressed formats, and also permit an interested party to access those non-optimized images, but never actually force them to load on the client’s page.

To that end, I’ve adopted the following policy:

Whenever I use a WebP image on a page, I will also have an identically-named original version of that image located right next to it, with only the file extension changed.

So, for example, suppose that I have a page at the URL extension /articles/some-article/, and there’s some image it loads at path /articles/some-article/image-name.webp. Then, there will always exist an original image that the user can access by going to the URL extension /articles/some-article/image-name.jpg, or /articles/some-article/image-name.png, or whatever the file extension of the original image might be.

Sure, this will probably take some guesswork on the part of the client to figure out which file extension the original image has. But this is intended to be an ironclad policy for determined users who really want an original image for abstruse reasons, and this is already a huge favor to provide to them. I can’t think of a way to make their experience even more streamlined than it is now without also impacting the page load times of the average client.

So, given that this is my goal, how do I get WebP images? It turns out it’s really hard.

My first instinct was to check Affinity or Adobe products, and it turns out none of the Affinity products have WebP exporting support. Adobe Photoshop does have WebP exporting support, but it’s expensive and I don’t have access to it currently. It’s certainly not an accessible option for the average person when exporting WebP files should be such a simple use case! So using something like Photoshop seems overkill.

I then turned to Paint.NET, which is one of the best pieces of raster image editing software out there. I really like it. It’s simple, doesn’t have a lot of bells and whistles, but just works so darn well. It turns out it has a plug-in (one that’s seemingly enabled by default?) that enables WebP exporting support. All you have to do is “Save As” your file as a WebP, and it will open a screen allowing you to choose different compression levels to export the file.

However, it turns out that this doesn’t actually let you shrink the number of pixels used in the image at all, placing a lower bound on how much this tool lets you compress things. I tried the method Paint.NET provides and it gave me quite horrible image quality at pretty high compression ratios.

It was then that I discovered Opti-WebP, an open source program that lets you convert JPEGs and PNGS into WebP files. Rather than let you specify a “compression level,” it lets you specify a maximum width and height for every image within a folder of images, then convert them all at once. This is very useful for batch processing, but I found it works really well even for converting one image at a time. Its only limitation is that it doesn’t let you make an image’s width or height go lower than 500 pixels, and I hope they remove this limitation in the future.

The results were fantastic. My self portrait on my homepage went from 1621 kB to 32 kB, and the two images of knots on the page went from 115 kB and 91 kB to 27 kB and 23 kB, respectively. The image of the Folded Polynomial thumbnail was the last image I compressed, and it went down from 131 kB to 63 kB without a noticeable drop in quality. The other images did drop in quality a bit, but for their display size it isn’t visible to the viewer (and I was willing to put up with a minor drop in visual quality from the get-go).

I’d call this optimization adventure a success! I would rather it were an easier process for the average person to do, but I’m happy I was able to pull it off. Hopefully I can turn this microblog post into a guide for people to follow in the future.

Now, I’ve just got to optimize my favicon image and my fonts, and all of my website’s assets will be fully optimized. That’s next on the roadmap.

And of course, once that’s done, I’ll have to figure out how to minify my CSS! That’s a later goal on the roadmap, too.

Blog post idea: making optimized and custom variable fonts is impossible

If you want to make your own custom variable font—or, even simpler, if you want to slightly modify an existing variable font—and convert it to a WOFF2 format, you have basically zero options.

This list of recommended font editors from r/typography doesn’t cut it, because none of them allow WOFF2 as an output or allow the use of variable fonts, let alone both at the same time. I’ve checked the entire list!

What we want:

  • FOSS
  • Reasonable pricetag (at most $60 or so)
  • Allows editing variable fonts
  • Allows exporting WOFF2 fonts while preserving variable font information
  • Cross-platform (i.e. works on Windows, MacOS, and Linux)

No editor on this list, nor any of the others I’ve come across while searching the internet, satisfies all 5 of these properties.

Two of the options, calligraphr and Scanahand, are handwriting converters. So that’s not even in the right category of what we’re looking for.

FontLab allows you to work with variable fonts and works on both MacOS and Windows, but it costs 500 euros and is proprietary software. One of the programs that comes bundled with it, TransType, says that it supports exporting to the WOFF format, but doesn’t explicitly say they support WOFF2. It’s unclear—do they think that saying WOFF is a target implies to the reader that WOFF2 is supported? It could also indicate their products are out of date and don’t support more recent font file formats! Given the massive pricetag associated with FontLab and the risk that TransType doesn’t actually support WOFF2, I think this is an absurd option.

The Reddit recommendation list for some reason lists Fontographer separately from FontLab, even though the former is an essentially deprecated earlier version of the latter.

TypeTool, the other FontLab-affiliated editor from the Reddit recommendation list, also doesn’t say that it works with variable fonts. Given how much FontLab advertises that capability, I think that implies TypeTool doesn’t have it.

FontCreator seems like the first one that actually can export both WOFF2 and variable fonts, and it has the reasonable pricetag of $49. They also offer more expensive versions of the program that go up to $199 in price, but you don’t need those to use WOFF2 and variable fonts. And it’s semi-cross-platform, working on both MacOS and Windows. So the only real problem with this product is that it’s proprietary software and doesn’t have Linux support! It checks all the other boxes.

BirdFont is FOSS and works on a ton of platforms (Windows, Linux, MacOS and BSD), and does support variable fonts, but only lets you export TTF, OTF and SVG fonts. WOFF2 is not supported. Only its “plus version” has variable font support, and at the time of writing its pricetag is $10.

FontSelf is a plugin for Adobe Illustrator. Their website is uniquely uninformative about what features their software has. Some posts from their communtity forum indicate that they do allow you to export WOFF2 files, but as of 2020, they didn’t seem to support variable fonts.

FontArk is free and runs in a browser, but it sketchily both requires an account to use and its website doesn’t seem to load properly. I’m not even going to link to it because I’m kind of concerned about what’s making that website fail to load and whether it’s something malicious.

Glyphr Studio is free and runs in a browser. It supports importing and exporting TTF, OTF, and WOFF files, but doesn’t say anything about WOFF2. It also explicitly says variable fonts are not supported.

Type 3.2 costs $15 and works on MacOS and Windows. They also support variable fonts. They don’t seem to support WOFF2, though, according to this one poorly-worded request for the feature from 2018. And since this particular program is no longer under development, it never will support WOFF2.

FontStruct is free and runs in a browser, but their website doesn’t explain what kinds of features it provides at all. Since it requires an account to use, I think it’s not even worth trying it to find out whether it provides variable font support or WOFF2 exporting.

Glyphs can do it, but it costs 299 euros. Its smaller, introductory-use program, Glyphs Mini, costs 49 euros but doesn’t support either variable fonts or WOFF2 exporting. Both are also proprietary software and only work on MacOS.

RoboFont does allow for working with variable fonts, but exporting to WOFF2 requires an extension. They also just… recommend that you do the conversion from TTF to WOFF2 using a FOSS Python library called FontTools. They provide the following script:

from fontTools.ttLib import TTFont

'''Generate WOFF2 from TTF or OTF font.'''

srcPath  = 'myFolder/myFont.ttf'
woff2Path = srcPath.replace('.ttf', '.woff2')

with TTFont(srcPath) as font:
    font.flavor = 'woff2'
    font.save(woff2Path)

And since RoboFont itself costs 500 euros and only works for MacOS, I think that seems like an incredibly crummy purchase for this use case. I haven’t yet tested FontTools to see if they preserve variable font axes when converting to WOFF2, so I’ll have to check and update this post later. (Their documentation doesn’t make it clear.)

There are hacky ways to do it with FontForge, which has extremely spotty variable font support. They do also support WOFF2 output. Anecdotally, when I tried to export a variable font as WOFF2, it completely removed the variable font axes, and I’ve been finding the online tutorials for those hacky workarounds hard to follow. So I’m not sure if this is a feasible route at all. I can say with absolute certainty that if it is feasible, it’s an inaccessible solution for the vast majority of people.

That exhausts the Reddit recommendation list. What else is out there?

There’s google-webfonts-helper, hosted here, that allows you to automatically convert Google fonts into WOFF2 files. That at least lets you do the optimizing part of making a font, if not the customizing part of it. If you managed to make a custom font without WOFF2 support and get it accepted into Google fonts, this could be a roundabout way to optimize your own custom font! But they don’t support variable fonts, so it’s useless for our purposes.

There’s a collection of programs called the Modular Font Editor K made by Fredrick Brennan that aren’t complete. They probably won’t ever be complete, judging by the fact that I haven’t seen updates to the docs GitHub repository in 3 years and updates to either the website or individual program repositories in 1 year.

There’s a program made by the Linebender organization, called Runebender, which is incomplete as well. And Linebender is the group that I’d most expect to care about making sure there’s a good font editor out there! But they’re mostly focused on their other projects at the moment, like Vello and Xilem—fantastic endeavors, but ones almost completely unrelated to font design. Runebender hasn’t had any updates (aside from dependency version bumps) since 2021, which was 4 years ago.

So we’re doomed. There’s nothing out there which I could use easily. Either FontForge or Glyphsapp would require a huge amount of effort, into either an open source project that doesn’t do what I need it to do, or a proprietary ecosystem I’d be locking my skillset into which only works on an OS I hate.

This situation is terrible. I suddenly understand why people flat out do not optimize their fonts—the barriers to entry are absurd.

What I’m going to attempt first is to try BirdFont for editing my variable fonts, and then FontTools for exporting for WOFF2. I’m going to cross my fingers that the latter doesn’t destroy all the variable font data.

Blog post idea: What I’m looking for in a mouse

I’ve recently started looking for a new mouse. I currently have either a Logitech M500s or a Logitech M500—I got it several years ago, forgot which one of those two models it was, and can’t tell which one it is since I can’t distinguish between the two models based on what’s inscribed on the mouse.

It’s a wired mouse, and I like that. It’s a durable mouse, and I like that. I don’t have any huge major complaints with it.

But I’ve realized recently that if I’m going to feel happy with the technology I use, I need to stop complaining about the bad things I can’t easily change, and start looking at the “good” things that I’ve been taking for granted and that might be able to be made better. That topic actually deserves a blog post of its own—I should return to it at some point.

There’s another blog post I’d like to write based on this one, which is that the tech hardware industry too aggressively engages in market segmentation of gamer products and white collar professionals’ products. I’m, uh, mostly writing this paragraph as a reminder to myself to write that blog post, too.

Anyways! Here’s a list of the things I’m looking for in a mouse:

  1. Wired. This is for several reasons, the main two being “having to think about charging the mouse at all” concerns, and latency concerns. If the mouse is not wired, it should be connectable via a wire to a computer for charging and use, with a compatible wire that is premium enough not to be damaged more easily than the wire affixed to a high-quality wired mouse, especially if shoved inside a backpack. The wire should also be flexible and light-weight enough to not impede movement, and also so that it folds up nicely enough to not take up lots of space in a backpack. It’s acceptable if I need to find a wire like that from a third party seller—I’m just uncertain whether such third party sellers exist, and so I don’t want to gamble on them existing if I can avoid it. I haven’t done enough research to know what kinds of third party wires are out there yet. Additionally, if the mouse can’t be wired, it in turns needs to have a low-latency plug-in receptor that can be stored inside the mouse itself, so I don’t lose it.
  2. A high polling rate, ideally 1000 Hz. Logitech mice (and indeed most non-gamer mice) have a standard polling rate of 125 Hz. That’s much too low to take full advantage of a 165 Hz monitor, like my Asus Zephyrus G14 (2023) has. I’ve tested the trackpads of 120 Hz, 144 Hz, and 165 Hz monitor laptops, and 165 Hz does feel noticeably better (though I don’t perceive nearly as much benefit going even further to a 240 Hz monitor). However, I noticed that my mouse didn’t actually feel like as large of a noticeable improvement. At the time, I shrugged that feeling off, assuming it was something about mouses intrinsically that gave me that feeling—maybe they’re so much more responsive by default that you don’t experience psychological benefits to the same degree with a high(er) refresh rate monitor? But in retrospect, they actually felt almost identical to the experience I got using a mouse on the 120 Hz monitors. So I think the 125 Hz polling rate was the bottleneck on my Asus Zephyrus G14 (2023), and I’m gambling that using a much faster polling rate will make a 165 Hz monitor feel wonderful. If I have a 1000 Hz polling rate, it should give me at worst a 1ms additive latency increase to all sources of input latency, compared to a 125 Hz mouse which provides at worst a 8ms increase. Given that battery life worsens in wireless mice with higher polling rates (and given that I might not be able to get a wired mouse that satisfies all my other desiderata), I think that makes 1000 Hz look like a good place on the tradeoff curve.
  3. No rubberized grip. My current mouse has slowly worn away the side grips in a way that is distinctly unpleasant. I want my mouse to be durable. Speaking of which…
  4. Longevity. I want this mouse to have an extraordinarily long lifespan, something on the order of 15 to 20 years. I’m not somebody who upgrades their gear frequently and it’s a huge mental strain to shift myself into “ah, I’ve gotta replace this longstanding linchpin of my life” mode. I gather from the internet that people in the mouse-enthusiast space seem to replace mice every 3-5 years—that’s just a rough impression, though, and I don’t know if they’re doing it by choice/to get better gear as it’s invented or because the products they use break frequently. I also don’t know, conditional upon it being that the products they use break frequently, whether it’s because mouse enthusiasts are more likely to use products with higher specs at the cost of longevity, or if all mice have low longevity.
  5. No rough plastic grips. I have a newer Xbox controller that intentionally uses a rough plastic surface for “increased grip,” and that is supposed to make it easier to hold if you’re sweating a lot from your hands. But honestly, I don’t sweat from my hands (basically ever, actually) and if I did, I would just take a break from using a controller or a mouse for a bit. The rough plastic grip thus provides me no benefit, but it does induce a noticeable downside: it’s very (mildly?) unpleasant to hold for longer play sessions. I suspect the same would be true of a mouse. What I’m looking for is a mouse made of a simple smooth plastic material, throwing grip quality to the wind. For my use case, this is ideal.
  6. No configuration. I want the mouse to work out of the box without having to ever download a configuration app from the company that manufactured it. Barring that, the app should be web-based and not require a download. Barring that, the app should not require an account to use. Barring that, the app should be able to be deleted afterwards without leaving behind any hidden “please leave us your feedback!” notifications forever after upon uninstall.
  7. Conditional upon the mouse having configuration, it should use on-board memory to store those settings. It should be able to carry my configuration from place to place, and from computer to computer.
  8. Bio-safe and environmentally friendly materials. I’m uncomfortable with the idea of PTFE skates, and I’d like a mouse that avoids them.
  9. Light color that makes it easy to spot dirt and clean it. Additionally, that color shouldn’t change as the mouse ages; for example, if the mouse is white, I don’t want it to contain bromide that will slowly turn it yellow.
  10. Durability. I store my mouse in my backpack, and it gets battered around a lot as I walk around. If that would cause the mouse to look visibly damaged or, worse yet, stop functioning properly, then that’s not the mouse for me.
  11. No metal components on the exterior. Precisely because I store my mouse in my backpack, I don’t want it banging into my other devices and damaging them. Metal components have a tendency to do this.
  12. No exterior lights. I hate glowing lights on my electronic devices, as it makes it harder to use them when I’m trying to adjust my eyes to a dim environment. In other words, I don’t want the thing that the gamers call “RGB.”
  13. The right size for my hand. It should fit perfectly.
  14. No haptic feedback. It’s horrible and I hate it.
  15. Moderate weight (not sure of the exact amount) approximately the same as my current mouse (the specs here claim it’s 144 grams). I don’t like ultra-light mice, but I also don’t want something so heavy that it induces arm strain. I’m very fortunate to not experience arm strain with the weight of my current mouse, so I want to stay in the same range. Ultra-light mice, by contrast, cause me to keep lifting them up on accident when using them—they feel wild and unpredictable and hard to calibrate my movements to.

Things I suspect would be good and would like to try:

  1. An ergonomic angled shape—of the various models of ergonomic mice, the Logitech MX Master 3’s angle looks like a good place to start experimenting. I’ve heard mixed reports about which styles of ergonomic design are actually good for long-term use, and this one looks the most promising among the three or so styles I’ve seen.
  2. A thumb rest. If I’m not using a grippable material on the sides, this becomes essential. It’ll (hopefully) serve the same purpose and stop my thumb’s muscles from having to hold it upright. I’m currently using the grippable material to add more friction to the tip of my thumb, avoiding the need for muscle use to hold it in place.
  3. A middling level of click feedback, around the same level as my current mouse. I don’t want to feel like left or right clicking is any discernable effort, and I also want distinct tactile feedback upon a successful click. My current mouse achieves those things, and I get the impression it’s a sweet spot which is hard to achieve.

Things I don’t need:

  1. Infinite scroll. It’s (slightly) bad to have a switch for this, and (moderately to highly) bad to have it engage automatically, like the Logitech MX Master 3 does. I want discretized scrolling on my scrollwheel and nothing else.
  2. Back and forward buttons on the side.
  3. A horizontal scroll wheel.
  4. Horizontal tilt of the scrollwheel.
  5. An extraordinarily low price point. I have a max budget of about $80, so I’m not constrained to budget mouses. Of course, I also don’t want to spend a fortune.
  6. Quiet clicking. I’m okay with any type of click noise levels.
  7. A maximalist design. Lots of higher-end mice have lots of metal or different-colored-plastic accents, and I don’t like these. I want something with minimalist aesthetics.

In summary: I’m someone who wants a simple mouse without frills, with the specs and guts of a gaming mouse, but with the exterior shape and approximate weight of a productivity mouse, and with an extreme prioritization of durability/longevity over premium-feeling materials.

A mouse like this doesn’t exist.

I’ve purchased the Keychron M6 for $55 (~$61 including tax) because it ticks a lot of these boxes. But there are many it doesn’t tick, too. It’s got the guts of a gaming mouse and the design sensibilities of a productivity mouse, which is so extraordinarily rare that I simply had to try it out—basically no one is catering that that particular market, and I’m very frustrated by the fact that there’s no competition in the same space.

Unfortunately, the Keychron M6 is wireless, and its low-latency connectors can’t be stored inside the mouse, making it likely they’ll get lost if I use them. It’s not as heavy as my current mouse (the specs here say it’s 78 grams, whereas my current mouse is 144 grams), which may pose a problem. It also has a lot more features than I need. But it checks so many other boxes that I’m feeling optimistic that those downsides will be masked by the more numerous upsides.

We’ll see how it goes!

Note to self: I’d like to turn this into a blog post that does a more extensive look at the market for mice, and gives people who might have different preferences from me educational materials to make their own informed purchases.

Draggable links

I’ve modified my buttons on the navigation bar so that draggable="false" on all <a> elements. Holy cow, this is a huge quality of life improvement.

Firefox developers seem to have put in quite thoughtful behavior for this attribute. It still permits clicking and dragging text that was pre-highlighted within an <a> element. It allows a link to be clicked by releasing the mouse button while still on the link after a drag, but also permits selecting characters within the link in the meantime during the drag just in case the user is planning to highlight text that starts halfway through the <a> element and then carry on to also highlight text in the next element on the page.

Most critically, it means that the active state for a link never gets triggered by clicking and dragging the link. That means I can reliably assume that the active state corresponds to a mouse down event that actually registered. This is fantastic! That means I can use a transition to make the styles associated with the active state go away after a minor delay, making the user feel like their click event registered properly.

Hooray for achieving good UI feedback without having to use JavaScript! (I’ve been looking for a way to do this for ages.)

I haven’t yet tested the behavior of draggable="false" in other browsers yet. Since most (all?) of this behavior is implementation defined, it would be good to verify that there aren’t any browsers that choose terrible behavior.

I’ve also enabled user-select=none on the active state of the buttons, since it gets rid of that text highlighting that Firefox enables when you click and drag a draggable="false" element. In other scenarios, that would be awesome behavior! It just isn’t quite what I’m looking for. I’ve intentionally allowed text highlighting of those buttons to still occur if the user starts highlighting from outside the button, which is great too.

Unfortunately, at the time of writing user-select has poor support in Safari. But that’s fine, since this is just a quality-of-life improvement! It’s okay if it doesn’t work across all browsers.

CSS is a form of page compression

I just realized that if you load all your CSS up front, it lets you compress a ton of visual information about future pages that you’d otherwise have to specify in the HTML.

I had this realization while looking at Cosma Shalizi’s Notebooks. They use this horrible iterated sequence of <div>s that are set to align left and mimic the appearance of some kind of list. But the actual result is incredibly HTML-heavy! His main Notebook page, when compressed, comes out to around 18kB. It could probably be half that if he just stripped out most of the HTML boilerplace and did his visual styling in some CSS, either inline within the HTML <head> or already located within the HTTP cache from the first page load as a separate CSS file.

I’ve gotta make a blog post about this at some point. If you’re committed to paying a higher up-front loading cost by linking a separate site-wide stylesheet on first page load, you can dramatically reduce the size of your other HTML files while also increasing the fraction of their elements which are semantic. This is such a good technique, and I haven’t seen it talked about anywhere on the internet before. I have a hunch there are a ton of amateur website developers who have no idea they could do something like what I’m describing and would really benefit from it.

iPhones can’t construct the DOM quickly

When I’m on an iPhone 13 mini, I notice that if I browse the internet and go to a website and navigate back and forth between a few pages, the navigation is noticeably faster if I use Safari’s “forward” and “back” buttons than if I click the links on each webpage to jump back and forth between pages. After the first two back-and-forths, the HTML and CSS should all be cached, right? Why is one of these things more performant than the other?

Like, I’m noticing delays of (nearly exactly) 500ms when I navigate back and forth on tutorial pages on w3schools. On a desktop, it’s near instantaneous when clicking into my Windows trackpad, which has basically 0 hardware input delay compared to tapping the trackpad, so on desktop I know that there isn’t anything aberrant happening. And on mobile, when I use Safari’s “forward” and “back” buttons, it feels like a little over 100ms of delay, which is par for the course with an iPhone if the sole source of input lag is the double-tap window or somesuch.

The BFCache is the answer (I think). It caches snapshots of the pages that have been seen before and actually holds onto the constructed CSSOM and DOM, rather than just the files it uses to construct them.

I suspect that on mobile the overhead of doing the parsing of cached HTML and CSS is so much larger than it is on desktop that it produces this 500ms delay. I’d love some way to benchmark and test this hypothesis, but I don’t know of any way to do benchmarking on an iOS device. Something for future me to look into, I suppose!