6 ms·
A flowing WebGL gradient, deconstructed
- mwkaufma 2y ago>> The mix function is an interpolation function that linearly interpolates between the two input colors using a blend factor between and ( in our case). >> A mix function for two colors works the same way, except we mix the color components. To mix two RGB colors, for example, we’d mix the red, green, and blue channels. Colorspace alert! mix != lerp in sRGB
- danwills 2y agoQuite right! I think if the values were linearized (~gamma 0.5) lerp might be mostly ok though, right? And what about doing rgb->hsv, then lerp, then hsv->rgb? I'm unclear whether that also needs linearization, or whether the gamma can maybe just be done to the 'v' component before lerping? Color is a surprisingly deep and fascinating topic, that's for sure! :)
- pornel 2y agoMixing of colors in an "objective" way like blur (lens focus) is a physical phenomenon, and should be done in linear color space. Subjective things, like color similarity and perception of brightness should be evaluated in perceptual color spaces. This includes sRGB (it's not very good at it, but it's trying). Gradients are weirdly in the middle. Smoothness and matching of colors are very subjective, but color interpolation is mathematically dubious in most perceptual color spaces, because √(avg(a+b)) ≠ avg(√(a) + √(b))
- mwkaufma 1y agoPerceptual colors -- both sRGB and HSB -- are nonlinear, so you can't expect linear combinations to produce meaningful results (they often "interpolate through mud"). If you just want optical phenomena, you can just convert to luminescence -- WegGL and other modern graphics APIs actually does this internally when you load or render textures, so all shaders are handling optically-linear data, which is why the shader-produced images in the post look better than the javascript gradients.
- eurekin 1y agoGoogled "luminescence" and OpenGL -- don't seem to return relevant results. Could you point to where it's described?
- mwkaufma 1y agoreplace/luminescence/luminance (thanks autocorrect). Legacy OpenGL APIs used to assume sRGB, so you had to specify GL_LUMINANCE for non-color 'intensity' maps (which couldn't be blitted to FBOs, e.g.). Modern OpenGL assumes linear color, so instead you have to specify sRGB on texture load to direct the driver to do colorspace conversion (e.g. GL_SRGB8 for typical RRGGBB byte triples). More info: https://www.khronos.org/opengl/wiki/Image_Format https://www.khronos.org/opengl/wiki/Image_Format
- virtualritz 1y agoLuminance[1], not luminescence[2]. [1] https://en.wikipedia.org/wiki/Luminance https://en.wikipedia.org/wiki/Luminance [2] https://en.wikipedia.org/wiki/Luminescence https://en.wikipedia.org/wiki/Luminescence
- mwkaufma 1y agoLol autocorrecttt ty
- itishappy 2y agoTo be fair, lerp still mixes colors, it just mixes ugly colors.
- immibis 1y ago(1,0,0) and (0,0,1) are each twice as bright, in terms of photons, as (0.5,0,0.5). If you quickly apply gamma=2 so the midpoint is (0.707,0,0.707) your gradient will look much better. Although other commenters suggested mixing in more complicated colour spaces.
- rikroots 1y agoI agree with the colorspace alert. Lerping red and blue in OKLAB or OKLCH colorspace produces a much nicer effect. Also, the article details linear interpolation, but I think there's a lot of fun to be had by introducing some easing functionality into the interpolation[1] - it's not difficult to achieve in code, even in shader code? I do disagree with the article about the need to do such work in the WebGL space. Modern CPUs are insanely fast nowadays, and browsers have put in a lot of work over the past few years to make the Canvas 2D API as performant as possible - including moving as much work as possible into the GPU behind the scenes. With a bit of effort, gradients can be animated in 2D canvases in many interesting ways![2][3] [1] - Easing a linear gradient in different color spaces: https://scrawl-v8.rikweb.org.uk/demo/canvas-003.html https://scrawl-v8.rikweb.org.uk/demo/canvas-003.html [2] - Animated gradient effect: https://codepen.io/kaliedarik/pen/poRLBLp https://codepen.io/kaliedarik/pen/poRLBLp [3] - Animating a gradient over a live video feed: https://codepen.io/kaliedarik/pen/MWMQyJZ https://codepen.io/kaliedarik/pen/MWMQyJZ
- meindnoch 1y agoWebGL/OpenGL doesn't use sRGB in the shaders. If you load an sRGB texture, or render to an sRGB surface, the API automatically applies the gamma- (or inverse gamma) curve, so the shader only ever sees the linear values.
- kookamamie 1y agoCorrect, it uses just numbers without any specific information about a colorspace being involved. Decoding and encoding sRGB happen during (texture) read and write stages.
- sly010 2y agoVery cool, but by css-rotating (skewY(-6deg)) the canvas at the last moment, you introduced aliasing on the border between the canvas and the rest of the page which kills the vibe. The browser can't automatically blend the canvas with the rest of the page. It's noticeable even on a brand new retina display. Maybe you could keep your canvas square and introduce the skew in the shader.
- danwills 2y agoGuess it depends on the browser as it looks sharp and free of aliasing for me, including when zooming in (Opera on Android)
- herpdyderp 2y ago- Safari: decent but still obviously present - Chrome: quite bad looking - Firefox: something in between (tested on macOS)
- andrewmcwatters 2y agoThe funny thing is, as far as I know, skewY is a virtual draw command in the WebKit family of rendering engines. It's "in the shader" already. For whatever reason, your browser's compositor is failing to anti-alias the rendering bounds of the canvas. I don't know why, though. I don't see the issue in Safari on my system.
- vanyle 1y agoAs a workaround, you can add a transparent border (border: 2px solid transparent) around the skewed element to have antialiasing (at least on chrome)
- herpdyderp 2y agoThe linked source code [0] doesn't seem to have any license attached to it. So how could I actually use this? Is it published as a package somewhere, like npm? 0. https://github.com/alexharri/website/blob/eb9551dd73126857045035b378b194dbf923c675/src/components/WebGLShader/shaders/fragment/final.ts https://github.com/alexharri/website/blob/eb9551dd7312685704...
- alexharri 1y agoHmm, good point. I’m abroad this week and don’t have a laptop, but I’ll look at adding an MIT licence to the shader itself when I’m home. (I don’t want the post contents to be MIT licensed, but the website code and examples should be) Thanks for raising this
- ikesau 2y agoJesus christ, this is such a polished article. Writing it must have taken at least 5 times as long as the shader! Have reblogged it and will refer back to it if ever I have some time to learn how to write them :)
- alexharri 1y agoThanks a lot! Yeah tons of work went into this post, I’ve been working on it since November. The shader itself was about 2-3 weeks of evenings to get the effect 90% of the way
- heinternets 1y agoThis looks very smooth. Would this be in 10 bit colour if the display supports it? I can't see any banding even when zoomed in Safari or Brave on a MBP with HDR display.
- polygot 1y agoReminds me of the JetBrains installer--not a bad reminder, I like the animations.
- mrmagoo17 1y agoWe need more articles as polished as this one. WebGL is a topic I wanted to get into for a while now but it's really difficult to find good content about it. Please keep sharing more experiments!
- flashblaze 1y agoCould not agree more. Heard good things about https://webgl2fundamentals.org/ https://webgl2fundamentals.org/, but I think it directly jumps into code without any background as such. At least that is what I felt. Not sure whether this is the only way to go about teaching WebGL.
- loige 1y agoThanks for sharing this one. I was looking for more material too!
- fallinditch 1y agoGreat post Alex, the gradients are lovely, I'm just starting to play around with webgl so the timing is perfect ...
- ww520 1y agoThis is a great read. Very instructional. Build from simple concepts and explain clearly. Kudos for the work.
- qiqitori 1y agoNice, it's a bit like an extremely modern version of the oldskool plasma effect!
- tomaskafka 1y agoThe whole time I was expecting just a 2d height field ("terrain") with animated noise as z, overlooked from an angle by an isometric camera, it could imo produce a same effect in a much simpler way (and without need for expensive blur), right?
- akx 1y agoThe blur here isn't expensive (as discussed in the article itself). Chances are sampling a 2D height field and projecting, etc. would be more expensive.
- grishka 1y agoThis effect kinda reminds me of the PSP menu background
- cloogshicer 1y agoFantastic post, very well written.
- kemuri 1y agoAmazing writeup, very inspiring!
- kookamamie 1y agoLooks pretty nice. I think there'd be a way of computing the blurriness without the iterative approach and without the aliasing produced in the process.
- smjburton 1y agoThis is so cool. These flowing gradients have a mesmerizing effect (almost like watching a lava lamp), and seem like a great way to add visual interest to a page. I'll have to play around with this and see if I can incorporate these flowing gradients into some of my web projects. Awesome job detailing the design process for this post.
- zparky 1y agoThis is an exemplary post and what I come to HN for.
- nyarlathotep_ 1y agoThis is an excellent article. Thanks for this.
- blabus 1y agoFantastic article but it'd at least be nice to credit Stripe for the original design.
- asdopf 1y agoIt is rare to see such detailed articles. And the result is very cool. Thanks