惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
CXSECURITY Database RSS Feed - CXSecurity.com
V
Visual Studio Blog
aimingoo的专栏
aimingoo的专栏
博客园_首页
C
Check Point Blog
T
Threatpost
SecWiki News
SecWiki News
宝玉的分享
宝玉的分享
AWS News Blog
AWS News Blog
博客园 - 三生石上(FineUI控件)
Scott Helme
Scott Helme
The Register - Security
The Register - Security
Cyberwarzone
Cyberwarzone
C
Cyber Attacks, Cyber Crime and Cyber Security
Know Your Adversary
Know Your Adversary
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
P
Proofpoint News Feed
I
InfoQ
WordPress大学
WordPress大学
A
Arctic Wolf
T
Threat Research - Cisco Blogs
大猫的无限游戏
大猫的无限游戏
J
Java Code Geeks
A
About on SuperTechFans
P
Palo Alto Networks Blog
博客园 - Franky
I
Intezer
T
Tenable Blog
S
Secure Thoughts
Project Zero
Project Zero
S
Securelist
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
H
Heimdal Security Blog
Google Online Security Blog
Google Online Security Blog
The Cloudflare Blog
云风的 BLOG
云风的 BLOG
Security Latest
Security Latest
M
MIT News - Artificial intelligence
Martin Fowler
Martin Fowler
H
Hackread – Cybersecurity News, Data Breaches, AI and More
B
Blog
MongoDB | Blog
MongoDB | Blog
Forbes - Security
Forbes - Security
Application and Cybersecurity Blog
Application and Cybersecurity Blog
MyScale Blog
MyScale Blog
The Last Watchdog
The Last Watchdog
F
Fortinet All Blogs
雷峰网
雷峰网
V2EX - 技术
V2EX - 技术

Lobsters

CIFSwitch: a non-universal Linux local root vulnerability RIPE NCC session fixation: poaching logins with an Atlas probe GNOME 2.20 but its Web Components Agentic Search for Context Engineering – Leonie Monigatti Garnix is shutting down [not OC] akashina.tngl.sh/jjc Concerning Emacs (and Jazz) Nitpicking the shell history scene in ‘Tron: Legacy’ What's cooking on SourceHut? Q2 2026 The tenth OpenPGP email summit Package managers that package package managers Clojure on Fennel part three: parsing WordPress at 23 Finding Miscompiles for Fun, Not Profit GitHub - creusot-rs/creusot: Creusot helps you prove your Rust code is correct. Announcing Rust 1.96.0 | Rust Blog A Love Letter to Neovim sqlite AGENTS.md Am I a Bad Friend? CSS vs. JavaScript • Josh W. Comeau Erlang Ecosystem Foundation - Supporting the BEAM community A brief note about slot access cost in Common Lisp Keyboard latency probe Rethinking the GNOME clipboard issues Back to the Building Blocks’ Building Blocks Tech Notes: Theseus: translating win32 to wasm Fast is better than slow Content-addressed Rust builds (or, what kache actually caches) Intent to Prototype: Embedding API Canada’s Bill C-22 and the security cost of collecting more data 5 PostgreSQL locking behaviors that trip people up okmij.org Stop advertising in your commits! | AksDev GitHub - mplsllc/macsurf: A modern web browser for Classic Mac OS 9 PowerPC. Real CSS3, ES5 JavaScript, native HTTPS — built with CodeWarrior on the Carbon API. Introducing DoomBench - Can Your Data Stack Run DOOM? What are some of your favourite developer tools? Building a Scalable Ingestion Pipeline with Temporal (Part 1) Converting shallow Git bundles into normal repositories Are you a member of any professional associations? What is a harmonic? An interactive comic about additive synthesis How Virtual Tables Work in the Itanium C++ ABI Using SwiftUI to Build a Mac-assed App in 2026 Rust (and Slint) on a jailbroken Kindle. ~jack/lambda-on-lambda - Serverless Haskell on AWS - sourcehut git Human proof for FOSS contributions Extremely simple internet radio controlled via IRC Announcing BABLR Splitting Konsole views from Helix to run tools | AksDev GitHub - yugr/rust-slides Serving files over HTTP three ways: synchronous, epoll, and io_uring update docs with information about building with build.py (#979) · astral-sh/python-build-standalone@c9c40c5 A Simple Makefile Tutorial On C extensions, portability, and alternative compilers Switching to Colemak | Pedro Alves Just How Bad Was The Intel IAPX432? Nix's Substituter List Is Not a Routing Table Accelerating copy_if using SIMD Lambda on Lambda: Serverless Haskell on AWS | Blog Announcing feed-repeat v1.0 Scaling Akvorado BMP RIB with sharding EYG news: A host of CLI improvements, new guides and new effects The social contract of writing JS Crossword C array types are weird; and related topics Flatpak will depend on systemd – OSnews Migrating from Go to Rust | corrode Rust Consulting A portentous reunion Vivado Licensing Options How my minimal, memory-safe Go rsync steers clear of vulnerabilities the entropy layer of a wavelet codec, on its own GitHub - nferhat/fht-compositor: A dynamic tiling Wayland compositor. Debian SE Linux and PinTheft Does bulk memmove speed up std::remove_if? (No.) 声明式部分更新 | Blog | Chrome for Developers Fully in-browser container builds Dianne Skoll's Web Site - Remind The Architecture of Open Source Applications (Volume 1)Berkeley DB Pardon MIE? - ironPeak Blog “Long-Term Support” doesn’t mean what you think Jira IS Turing-Complete May I recommend thinking of Emacs as your Fortress of Solitude hershey Floodgap Gopher-HTTP gateway gopher://thelambdalab.xyz/1cuneiforth/ HP QuickWeb, Singular And Pointless That one time I used Go panics for flow control A new suite of modern tools coming for editing and publishing RFCs From the Tabletop… The Digital Antiquarian Building a Host-Tuned GCC to Make GCC Compile Faster Are we self-sovereign PKI yet? Claw Patrol: an open-source security firewall for agents | Deno Revised^7 Report on Scheme, Large: Procedural Fascicle Draft is now public A Network Allow-List Won't Stop Exfiltration — André Graf From AFSK to Goertzel – µArt.cz Software For My New Home Server Introducing Neptune: Direct3D virtualization for QEMU AI Agent Bankrupted Their Operator While Trying to Scan DN42 - Lan Tian @ Blog mimalloc: A new, high-performance, scalable memory allocator for the modern era Making wl_shm fast The Soul of Maintaining a New Machine - Third Draft | Books in Progress What is Git made of?
Color picking Oklch for mortals – Hugo Daniel
Hugo Daniel · 2026-06-17 · via Lobsters

Perceptual color picking using the classic color picker

Complicated things

Some people really like complicated things, either because it makes them feel smart(er) or because it can be used as a flex for skill (or a masqueraded impression of skill), or because whatever l33tz0r reason.

Oklch is one such thing, it is complicated (at least for me), and screw it i'm fine with good old #hexcodes and coder colors everywhere. But the design community in social networks and other loud mediums embraced Oklch like it was the last coca-cola in the desert, if you are not using it you are outdated and do not have enough skillz.

To get a feel of how mind bending Oklch is, go ahead and play around with the (official?) Oklch color picker in https://oklch.com/ and then get back here if you are still in the mood.

Truth is there's actually something useful here in Oklch, and in this post I'll try a top-down approach to make it evident.

ELI5 Oklch

I am a passionate color picker creator, and I happily went down this Oklch rabbit hole, reading specs and trying to understand the matricial nature of colors in technical blog posts. A couple of years have passed, those ideas have matured, and here we go again.

Perceptual is key here, it means if you set the Lightness to 60% in Oklch, the subsequent colors you chose will all look perceptually 60% bright to a human being regardless of what color you pick.

This is of course limited by our ability to see colors in dark situations or in very bright situations, the amount of colors we can perceive changes drastically in different lighting scenarios, and this is what Oklch exposes to us.

The perception aspect of picking colors.

WTF is Chroma?

In this perceptual scenario where you can see different amounts of colors in different lighting values I like to think of chroma as the "amount of color".

If you like to be a bit more technical, then think of it as the amount of color if you remove the grayscale from it. How much color is left if compared to white, gray or black?

This varies with your monitor, some monitors can display more colors than others, Apple supports a large amount of colors, others support a bit less and others way more. If you are a developer you can check the range available in browsers using this media query.

When we are in perception land the term "chroma" is used, and in our perception "the amount of color" (chroma) is 0 when the Lightness is full back (we can't see colors in pitch black), and also the amount of color is 0 when the Lightness is full white (we can't see colors in full max lightness), then for the values in the middle of black and white the amount of color we can see varies, in a perfect lighting scenario we can see the maximum amount of color possible.

Chroma also varies with the color itself (hue), our eyes are amazingly well trained to see a lot of greens in some lighting scenarios, so we have a lot of chroma to chose from in greens when the light is good.

In the land of sight the three-eyed man is king

That's what LCH stands for: Lightness (how much light?), Chroma (how much color?), Hue (how many colors?) we can see at each of these values.

The "Ok" is just a technical feature that comes from the base work Oklch sits on top of (Oklab color space as the base for this representation). Essentially it means things are perceptually uniform, so we can move any one of the L,C,H components while the other two actually hold steady, even as we drift across the range.

Enhancing the classic color picker

Coming from a coder perspective, the thing that attracts me the most is not in exposing all of the Oklch features all at once, but instead on how to enhance and blend them in a familiar color picker.

The classic color picker

A hue wheel and a triangle:

My version of the classic color picker

In the classical hex color scenario I'm a huge fan of the triangle as a picker tool because it tells the story of how we go from the fully saturated color (in the right cusp of the triangle) up to either white (top left) or black (bottom left) using its edges, and in the body of the triangle all the values in between.

Arrows climb from the saturated cusp up to white and down to black, with a line splitting the triangle into tints and shades

It logically splits nicely the amount of shades and tints we have for each color.

Unfortunately these logic parts don't always hold when we deal with the human eye and its curves and discontinuities that naturally arise from biological perception.

Next up I am going to cram Oklch into this canonical color picker, in an attempt to expose the perception axis that comes with the former while also retaining the familiarity of the latter.

Adaptations

I don't want to bring in everything at once, the idea is to keep the spirit of the color picker but with slight changes that are useful when dealing with the new set of tools that Oklch gives us.

Hue wheel

The amount of colors we see is not the same for all colors, and it varies with the lighting, to make this stand out, I am adapting the color wheel that typically represents the hue to also show the amount of color available at each hue degree (for the chosen lighting).

The hue wheel, its ring coloured by hue with the chroma triangle inside

Triangle

The triangle is a nice tool to have in Oklch, however it no longer represents a clean split between shades and tints because the mapping is no longer linear nor balanced (blame our eyes). I am going to stretch the available shades and tints in the triangle, so that all of them are visible, getting rid of the shades/tints balance is a tradeoff. The gradient in the triangle is no longer perfect.

A cool thing to maximize intuition when picking Oklch is to make Lighting the king. This means that chroma and hue are dynamic, but Lighting stays the fixed. In the triangle this means that we go from max lighting to min lighting by just traversing vertically.

A vertical lightness axis through the triangle: up is lighter, down is darker

And when the chroma starts to get lost because the lighting is already too aggressive for our eyes to keep track of all that color amount, we see that in the triangle by navigating through its edges instead of its body. This is a cool thing, the triangle edges communicates the amount of lighting interval we have to work with while keeping the same amount of color. Maximum color is only achieved in a very specific perfect lighting amount (the cusp).

Sliders

Naturally sliders show up for Lighting, Chroma and Hue. These are orthogonal to Lighting (remember, lighting is king as an intuitive first approach to picking Oklch), which means that Chroma will have a non-fixed scale by default and adapts as we change lighting and hue.

The perfect lighting is shown with a dot, if clicked the lighting is adjusted to the one that gives us the max amount of chroma possible for the chosen Hue.

Then to flip the script, as a long tail feature I also want to allow chroma to be locked and set as the king. When that happens the chroma scale is updated to be fixed, and the lighting is the one moving instead. Also as a helper, the colors we cannot have the needed amount will be phased out of the hue slider.

The Lightness, Chroma and Hue sliders, with the chroma lock and the perfect-lightness dot

We are all connected

Everything moves as we explore colors around.

And here with the locked chroma version:

Conclusion

In this post I tried to adapt the color picker to Oklch, making it more organic and dynamic, while also introducing a rough working guideline for the concepts behind it by treating L as if it was the lighting in the room, even though it's really a property of the color (hey in my defence it was in the ELI5 section). I also just ignored and jumped over a few important concepts along the way (color spaces, iluminants, gamut mapping, color management/correction, etc...) because the point was really the possible color picker experience through one of many infinite possible metaphors for it.

You can play with it here

It is also available as a Figma plugin.