The 0.5 sRGB grey (aka 50%, 0x7f or 0x80) is right smack at the grey value most people would pick as the halfway point between black and white.
A linear 0.5 grey is way off from that, I think way too bright, but I’m about 10 months off working on this and to be honest this is one of those things like east/west or left/right that I can never remember without working out the math on a piece of paper. Too bright or too dark for sure, not at all a medium grey.
It took my team a long time to not think of linear and sRGB interpolation in terms of good vs. bad, right vs. wrong, modern vs. legacy, etc. There are good uses for interpolating in both color spaces, and an a variety of others like HSL, HSV, CMY, perceptual spaces. Sorry for continuing the problem. There’s no ugly color space; they all have good uses.
You are right though that schemes like sRGB are a better fit for perceptual brightness. As you say the slider works the way people expect it to and you don’t need so many gray levels. 8 bit linear light shows posterization at some levels and is shaded too finely at others. If you want good results with linear light and common tools you need 16 bits of grey.
In linear interpolation, there's too much white and not enough black. That's why gamma correction/sRGB exists after all: if you encode the linear value directly, you waste most of the space on white values you can barely tell apart.
sRGB: https://i.imgur.com/dbn3TM6.png Lab (linear): https://i.imgur.com/dCXzbF3.png
Please download the images and view in a proper image viewer. For some reason at least on my Windows machine Google Chrome adds horrible color banding to both images.
This is my code:
import imageio
import numpy as np
import colour
quantize_with_dither = lambda im: np.clip(im * (2**8-1) + np.random.uniform(size=im.shape), 0, 2**8-1).astype(np.uint8)
srgb_black = [0, 0, 0]
srgb_white = [1, 1, 1]
srgb_grad = np.linspace(srgb_black, srgb_white, 500) * np.ones((200, 1, 3))
imageio.imwrite("srgb.tif", quantize_with_dither(srgb_grad))
lab_black = colour.XYZ_to_Lab(colour.sRGB_to_XYZ(srgb_black))
lab_white = colour.XYZ_to_Lab(colour.sRGB_to_XYZ(srgb_white))
lab_grad = np.linspace(lab_black, lab_white, 500) * np.ones((200, 1, 3))
lab_grad_in_srgb = colour.XYZ_to_sRGB(colour.Lab_to_XYZ(lab_grad))
imageio.imwrite("lab.tif", quantize_with_dither(lab_grad_in_srgb))
I didn't trust imageio's correct handling of gamma/colorspaces so I just wrote out the raw sRGB coefficients as 8-bit TIFF and used imagemagick to convert to PNG, telling it that the input is in sRGB: magick convert lab.tif -set colorspace sRGB -depth 8 lab.png
magick convert srgb.tif -set colorspace sRGB -depth 8 srgb.pngGamma encoding is lin_to_srgb(c) = c*12.92 if c<=0.0031308 else 1.055*c**(1/2.4)-0.055. Since lin_to_srgb(0.214)=0.5, in a linear ramp, the middle gray (808080) pixel should occur about 20% of the way through.
https://i.imgur.com/jeM4RCu.png
top: linear_to_srgb(x)
middle: x
bottom: srgb_to_linear(x)