Pekka Väänänen
banner
pekkavaa.bsky.social
Pekka Väänänen
@pekkavaa.bsky.social
Avid reader, computer graphics fan and atmospheric jungle beats enjoyer. Demoscene: cce/Peisik.

Blog at https://30fps.net/
Alright actually there was a linear regression fit to the hue experiment data, presumably via the "reg post" and "reg pre" buttons in the screenshot earlier. Constraints such as constant lightness was verified visually by plotting other datasets. Makes sense Oklab could improve on it.
November 27, 2025 at 2:00 PM
Also, had to wait until page 107 before I found the first joke. And uh, the IPT color space based on the experimental data was interactively tweaked by hand in a GUI instead of a numerical fit?
November 27, 2025 at 2:00 PM
It's lovely when I see dedication to an experiment: keeping a CRT on for a month to avoid affecting its color reproduction. From Fritz Ebner's 1998 dissertation: repository.rit.edu/theses/2858/ The same hue matching experiment data was used by Björn Ottosson when creating the Oklab color space.
November 27, 2025 at 2:00 PM
I recently translated my book manuscript (it's about palettes) to Typst and been pretty happy so far. Today instead of writing I procrastinated by moving code snippets' line numbers over the left margin to salvage three extra characters of horizontal space😃
November 21, 2025 at 6:56 PM
It's a good question. Fabien Sanglard's book suggests the palette conversion was done on a NeXT workstation before being transferred to a DOS machine for a Deluxe Paint clean up. But no mention of the palette origins.
October 31, 2025 at 9:14 AM
What if Doom's "colormap" was computed in Oklab instead of RGB?

A short article about it on my site: 30fps.net/pages/doom-o...
October 30, 2025 at 8:32 AM
Relatable😑
From: doomwiki.org/wiki/Carmack...
The line in question: github.com/linguica/dmu...
October 27, 2025 at 8:32 AM
A new article: Why CIELAB doesn’t improve median cut

It's a common misconception that CIELAB will instantly improve palettes produced by the median cut algorithm. Unfortunately it's not that simple.

📜 30fps.net/pages/median...

It gets pretty technical but I added some context to the intro.
October 24, 2025 at 12:50 PM
New article on my site: Better sRGB to greyscale conversion

The commonly used greyscale formula is slightly off when computed in gamma space but can it be fixed?

📜 30fps.net/pages/better...
October 13, 2025 at 5:56 PM
My new visualization for color quantization algorithm execution, though still unfinished. Attached is the result for a simple 12-color median cut. Each box contains a subset of image colors with the X-axis as a chosen sort axis; Y is PCA for plotting. See how e.g. box 3 has unwanted greens in it.
October 7, 2025 at 7:32 AM
A neat little paper from 2006 "A Median Cut Algorithm for Light Probe Sampling" where an environment map is compressed to a set of point lights using the median cut algorithm. Seems like a precursor to another 2007 paper where others cast the task as density estimation.
September 24, 2025 at 5:56 PM
There's a classic color transfer technique from 2001: convert source and target image to a decorrelated color space (Oklab works fine), make target's mean and stddev match the source, convert back to RGB. Using a palette image as a target is no problem :)

Paper: home.cis.rit.edu/~cnspci/refe...
September 10, 2025 at 7:32 AM
A short article on mapping pixel colors to the PICO-8 palette in the CAM16-UCS color space. Surprisingly, it didn't do much better than Oklab, which is derived from CAM16.

📜 30fps.net/pages/percep...
September 8, 2025 at 11:52 AM
Got familiar with NumPy's structured arrays today. It's basically an Array Of Structs, where fields can of course be different types. Could be useful with binary files, but I used it to organize code. Adding a "record array" allows a nice `colors.r` access syntax.

numpy.org/doc/stable/u...
September 3, 2025 at 6:52 PM
I finally added alpha support to VariQuant. It took a lot of head-scratching to make it work with both RGB and L*a*b*😅 I premultiply the alpha and handle it like the other color channels. For RGBA color distance I'm using Kornel Lesiński's clever formula: stackoverflow.com/a/8796867
August 27, 2025 at 5:56 PM
Gamma correction seems to be one of those things where the more you read the less you know. Is it really true that the NTSC standard assumes a CRT gamma of 2.5 but encoding is done with 1/2.2 instead to add a little contrast boost? From Charles Poynton's 2002 article at poynton.ca/notes/colour...
August 26, 2025 at 5:56 PM
I've been improving my "VariQuant" color quantization tool. It now supports more color spaces, locally optimal cuts that guarantee the next color cluster split is the one that gives greatest decrease in error, luminance weight scaling, HyAB distance, A/B toggle, and palettes can be saved as PNGs :)
August 22, 2025 at 5:56 PM
Here's a zoomed in portion of values near zero (note the uneven axis scaling) where cbrt(x) (green) shows a big difference to the almost-linear cbrt(x+b)-cbrt(b) (red). The blue vertical line is at x=1/255, for reference.

One of the authors discusses this on HN: news.ycombinator.com/item?id=2552...
August 20, 2025 at 5:56 PM
I was studying the "XYB" color space today. It uses a cube root for gamma correction but instead of a linear "toe" like in sRGB, there's a bias in the inputs that gets subtracted afterwards. Should be simpler to compute. I was surprised how the curve doesn't reach 1.0 at the end!
August 20, 2025 at 5:56 PM
I've been improving my experimental color quantization tool. It now supports separate palette design and pixel mapping color spaces with their own lightness weights. I added that palette visualization today and it's fun to play around the parameters and see it evolve.
August 13, 2025 at 11:52 AM
I wrote earlier of a PCA approximation which worked surprisingly well. After some discussions, I now finally have a clear counter example where it fails. If the channels are uncorrelated, the result is an average that's clearly wrong.

Updated the article: 30fps.net/pages/approx...
August 7, 2025 at 11:52 AM
Apparently in Python's dataclasses any member not set via the constructor gets ignored in equality checks. Both asserts pass in the code included code snippet. I put this example to a gist of it so I can reference it in the future: gist.github.com/pekkavaa/4a1...
August 5, 2025 at 5:56 PM
Interesting to see how in the original "The Irradiance Volume" paper from 1998 they stored irradiance per discrete direction instead of smooth spherical harmonics. Makes sense of course, it's basically a cubemap.
July 22, 2025 at 5:56 PM
A new article on my site: Approximate first principal component

It describes simple trick to estimate the direction of most spread of a bunch of points without running a full PCA. Includes my Python re-implementation and plots of some quick tests.

30fps.net/pages/approx...
July 17, 2025 at 5:56 PM
I wanted to try using some experimental color quantization algorithms for a real project (on the N64) so I started working on a little GUI tool. Interactive parameter editing is already fun with that single color count slider😀 And tk's "Professional Motif Look" keeps expectations at the right level.
July 17, 2025 at 11:52 AM