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/
Pinned
Hello I'm Pekka and I do experiments in computer graphics and machine learning. I mostly post about my coding projects, my blog or books I'm reading. I read a lot.

For the last couple of years I've gotten into #N64 coding. It's great for low-level graphics! I also keep an eye on #demoscene stuff.
The Wikipedia page on Color appearance models at en.wikipedia.org/wiki/Color_a... surprisingly good: to the point but still feels comprehensive. Includes a quick comparison of different perceptual color spaces.
Color appearance model - Wikipedia
A color appearance model (CAM) is a mathematical model that seeks to describe the perceptual aspects of human color vision, i.e. viewing conditions under which the appearance of a color does not…
en.wikipedia.org
November 17, 2025 at 8:32 AM
Reposted by Pekka Väänänen
via the magic of laion_clap embeddings and umap, my live coding thingy has a sample browser at last!
October 31, 2025 at 6:27 PM
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
Reposted by Pekka Väänänen
Silent Hill 4 uses interesting volumetric light / light glare approximation.
Instead of drawing sprites at each light source, full-screen subdivided plane with procedural vertex colors is rendered and blended with game frame.
#gamedev
October 29, 2025 at 12:53 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
Wow I didn't know Python has cute chained comparisons: "e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false)."

docs.python.org/3/reference/...
6. Expressions
This chapter explains the meaning of the elements of expressions in Python. Syntax Notes: In this and the following chapters, extended BNF notation will be used to describe syntax, not lexical anal...
docs.python.org
October 23, 2025 at 11:52 AM
A fun look into Source 2's precomputed visibility from a mapper's perspective: www.youtube.com/watch?v=XLT2... It's not a BSP anymore but an octree whose nodes get grouped into clusters. Spoiler: Visleaks can still happen😀
CS2 Mapping Academy #10 - VIS Optimization (Counter Strike 2)
VIS is one of the most important facets of Source 2 level design. Learning how the engine optimizes your maps is important so that everyone can enjoy it! Learn how to take control of VIS in this…
www.youtube.com
October 21, 2025 at 11:52 AM
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
Reposted by Pekka Väänänen
New blog post! In "Billions of triangles in minutes" we'll walk through hierarchical cluster level of detail generation of, well, billions of triangles in minutes. Reposts welcome!

zeux.io/2025/09/30/b...
September 30, 2025 at 5:40 PM
I wrote a simple box collision resolution code today and made the classic mistake of not checking if box_a != box_b before testing for overlap. This was the bug that made me post my first programming question online 20 years ago or so! I was unaware of the <> Basic "not equals" operator at the time.
October 3, 2025 at 5:56 PM
Tried to be clever with some NumPy array partitioning code and exchange two values with the "x[i], x[j] = x[j], x[i]" idiom. Nope: got to use "x[[i,j]] = x[[j, i]]" instead, see stackoverflow.com/a/47951813
Swap two values in a numpy array.
Is there something more efficient than the following code to swap two values of a numpy 1D array? input_seq = arange(64) ix1 = randint(len(input_seq)) ixs2 = randint(len(input_seq)) temp = input...
stackoverflow.com
September 26, 2025 at 11:52 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
Reposted by Pekka Väänänen
Got my flight sim taking keyboard input and using that to drive the aerodynamic control surfaces. One step closer to take-off! #gamedev #flightsim #physics 🛩️
September 18, 2025 at 1:09 PM
Reposted by Pekka Väänänen
N64brew user @boxingbruin.bsky.social continues to showcase more work that they've done for their Pickle64 project, their action-RPG platformer hybrid with dark souls-like boss fights.
September 14, 2025 at 11:36 AM
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
Reposted by Pekka Väänänen
Some fun with shader-based debug drawing: here I'm drawing an arrow for each path taken in the path tracer, starting with the pixel under the mouse cursor.
August 31, 2025 at 11:10 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
Reposted by Pekka Väänänen
meshoptimizer v0.25 is out! Featuring new simplification function that optimizes positions and attributes for appearance, experimental permissive mode to simplify faceted regions with selective seam preservation, regularization option for improved tessellation quality and deformation, multiple ...
August 20, 2025 at 3:56 PM