# WebGL-kleurverlopen en shaders, uitgelegd voor ontwerpers

[Canonical HTML page](https://gradiently.design/nl/guide/shader-gradients)

Sommige kleurverlopen op het web drijven, gloeien en glinsteren zoals geen afbeelding of CSS-regel dat kan. Het zijn shaders. Zo werken ze, in woorden waar een ontwerper iets mee kan.

## The short version

- Een WebGL-kleurverloop wordt getekend door een shader: een klein programma dat op de grafische chip draait en de kleur van elke pixel bepaalt, vaak vele keren per seconde.
- Omdat een shader kleur uitrekent met wiskunde in plaats van een opgeslagen beeld te lezen, kan hij op elk formaat beweging, ruis, korrel en licht toevoegen zonder dat het bestand groter wordt.
- Shader-kleurverlopen kosten meer rekenkracht dan CSS-kleurverlopen, dus een goede pauzeert buiten beeld, respecteert verminderde beweging en heeft een eenvoudige CSS-terugvaloptie.
- Gebruik CSS voor statische verlopen en simpele beweging, en grijp naar een shader wanneer de textuur, diepte of beweging het punt van het ontwerp is.

Een **WebGL-kleurverloop** is een kleurverloop dat je browser schildert met een shader: een kort programma dat op de grafische chip (de GPU) draait en voor elke pixel op het scherm één vraag beantwoordt: ‘welke kleur ben je op dit moment?’ Een CSS-kleurverloop is een vast recept van kleuren en posities. Een shader is een formule, dus hij kan de kleur buigen, korrelen, laten gloeien en bewegen, en dat zestig keer per seconde opnieuw doen, zonder dat er een afbeelding wordt gedownload.

Je hoeft geen shaders te schrijven om ermee te ontwerpen, maar weten wat ze wel en niet kunnen helpt je een developer te briefen, een tool te beoordelen en te beslissen wanneer een gewoon [CSS-kleurverloop](https://gradiently.design/nl/guide/css-linear-gradient) de betere keuze is.

## Wat een shader is, in gewone woorden

WebGL is de manier waarop de browser met de GPU praat, en elke WebGL-tekening gebruikt twee soorten shaders. De **vertex shader** plaatst vormen; voor een schermvullend kleurverloop is dat gewoon één rechthoek die het canvas bedekt. De **fragment shader** doet het interessante werk: hij draait één keer per pixel, krijgt de positie van die pixel en de huidige tijd, en geeft een kleur terug.

Omdat de GPU duizenden van deze programmaatjes parallel uitvoert, wordt een formule die in gewone code traag zou zijn goedkoop genoeg om elk frame opnieuw te tekenen. Hier is een complete fragment shader voor een kleurverloop in drie kleuren waarvan de grens zachtjes meedeint in de tijd, met een regel korrel erbovenop.

```text
#version 300 es
precision highp float;

uniform vec2 u_resolution;   // canvas size in pixels
uniform float u_time;        // seconds since start
out vec4 outColor;

void main() {
  vec2 uv = gl_FragCoord.xy / u_resolution;   // 0 to 1 across the canvas

  vec3 night  = vec3(0.07, 0.04, 0.18);  // #120a2e
  vec3 violet = vec3(0.49, 0.23, 0.93);  // #7c3aed
  vec3 coral  = vec3(0.98, 0.45, 0.45);  // #fa7373

  // A slow wave bends the blend so the colours drift.
  float wave = sin(u_time * 0.3 + uv.x * 3.0) * 0.08;
  float t = clamp(uv.y + wave, 0.0, 1.0);

  vec3 color = mix(night, violet, smoothstep(0.0, 0.6, t));
  color = mix(color, coral, smoothstep(0.55, 1.0, t));

  // Fine grain: a different random value per pixel, per frame.
  float n = fract(sin(dot(gl_FragCoord.xy + u_time, vec2(12.9898, 78.233))) * 43758.5453);
  color += (n - 0.5) * 0.04;

  outColor = vec4(color, 1.0);
}
```

Een WebGL2-fragment shader in GLSL. `smoothstep` verzacht elke overgang, de sinusgolf verplaatst de grens, en de laatste regels voegen korrel toe die ook banding verbergt.

- Nachtondergrond: #120a2e
- Violet: #7c3aed
- Koraal: #fa7373

De drie kleuren in de shader. GLSL schrijft kleur als 0 tot 1 per kanaal, dus #7c3aed wordt 124, 58 en 237 gedeeld door 255.

## Hoe een shader-kleurverloop verschilt van een CSS-kleurverloop

Een CSS-kleurverloop is declaratief: je somt kleuren en posities op, en de browser schildert ze. Meestal is het het juiste gereedschap. Een shader is procedureel: je beschrijft hoe kleur zich moet gedragen, en de wiskunde levert haar. Dat verschil zie je terug in wat elk kan en wat elk kost.

| Aspect | CSS-kleurverloop | Shader-kleurverloop |
| --- | --- | --- |
| Bepaald door | Kleuren, stops en een richting | Een formule die per pixel draait |
| Beweging | Positie of grootte verplaatsen, of geanimeerde custom properties | Alles wat je in wiskunde kunt uitdrukken, elk frame |
| Textuur en korrel | Vraagt een extra laag, zoals SVG-ruis | Ingebouwd, en het kan bewegen |
| Rekenkosten | Heel laag zodra het is geschilderd | Houdt de GPU bezig zolang het animeert |
| Werkt zonder JavaScript | Ja | Nee: een script zet het canvas op |
| Werkt in e-mail | Deels, afhankelijk van het programma | Nee |

Geen van beide is in het algemeen beter. Elk wint bij een andere taak.

De CSS-terugvaloptie: `linear-gradient(180deg, #120a2e 0%, #4c1d95 40%, #7c3aed 62%, #fa7373 100%)`

Hetzelfde palet als stilstaand CSS-kleurverloop. Elke shaderachtergrond hoort zo’n terugvaloptie mee te leveren, getoond voordat de shader start en telkens wanneer hij niet kan draaien.

## Waarom shader-kleurverlopen levend aanvoelen

Drie technieken doen het meeste werk, en je zult developers ze horen noemen. **Ruis** is vloeiende willekeur: in plaats van een recht verloop volgt de kleur een landschap van zachte heuvels, en daarom ogen shader-kleurverlopen organisch in plaats van geometrisch. **Domain warping** voert ruis terug in zichzelf, zodat de kleur vouwt en wervelt als inkt in water. **Tijd** schuift het hele landschap op, en dat maakt van een stilstaand vlak een drijvende beweging.

- **Korrel** kan per pixel en per frame worden gegenereerd, dus hij glinstert als film in plaats van stil te liggen als een textuur die erbovenop is gelegd. Zie [korrelige kleurverlopen](https://gradiently.design/nl/guide/grainy-gradients).
- **Licht** kun je met een paar regels nabootsen: een hooglicht dat de ruis volgt, leest als glans op zijde of glas.
- **Resolutie** is gratis. De formule draait op de grootte van het scherm, dus een shader-kleurverloop blijft met dezelfde code scherp op een telefoon en op een 4K-monitor.
- **Banding** is makkelijker te verslaan, omdat de shader een beetje dither kan toevoegen voordat de kleur naar 8 bits wordt afgerond. [Banding in kleurverlopen](https://gradiently.design/nl/guide/gradient-banding) legt uit waarom dat ertoe doet.

### Gebruik CSS wanneer

- Het kleurverloop stilstaat, of op één simpele manier beweegt
- Het achter veel tekst op een lange pagina zit
- Het moet werken met scripts uit, of in e-mail
- Je het in één regel code nodig hebt

### Gebruik een shader wanneer

- De beweging of textuur het punt van het ontwerp is
- Het om één hero gaat, niet om elke sectie
- Je korrel, glans of stroming wilt die CSS niet kan nabootsen
- Je een CSS-terugvaloptie en een pauze kunt leveren

## De kosten van een bewegende WebGL-achtergrond

Een shader die animeert, houdt de GPU aan het werk zolang hij in beeld is. Op een desktop merk je dat nauwelijks. Op een oudere telefoon kan het warme handen betekenen, een batterij die leegloopt en een pagina die minder soepel scrolt. De kosten schalen mee met het aantal pixels en de complexiteit van de formule, dus een schermvullende shader op een scherm met hoge pixeldichtheid is het zwaarste geval. [Vertragen kleurverlopen een website](https://gradiently.design/nl/guide/css-gradient-performance) behandelt hoe je het meet.

1. **Toon eerst de CSS-terugvaloptie** Schilder het stilstaande kleurverloop meteen en laat het canvas invervagen zodra de shader zijn eerste frame heeft getekend. Niemand hoort een leeg vak te zien.
2. **Controleer of WebGL beschikbaar is** Sommige browsers, apparaten en privacy-instellingen zetten het uit. Kan de context niet worden aangemaakt, houd dan de terugvaloptie.
3. **Respecteer verminderde beweging** Vraagt de bezoeker om minder beweging, teken dan één frame en stop. [Verminderde beweging](https://gradiently.design/nl/guide/reduced-motion) legt de instelling uit.
4. **Pauzeer wanneer het niet te zien is** Stop de animatielus wanneer het canvas uit beeld scrolt of het tabblad verborgen is.
5. **Render op een verstandige resolutie** Zachte kleurverlopen hebben zelden elke apparaatpixel nodig. Op halve resolutie tekenen en de browser laten opschalen kan veel werk schelen voor een vervaging waarin je het verschil niet ziet.

```js
const canvas = document.querySelector('#hero-canvas')
const gl = canvas.getContext('webgl2')
const still = matchMedia('(prefers-reduced-motion: reduce)').matches
let visible = true

if (!gl) {
  canvas.remove() // the CSS gradient behind it stays
} else {
  new IntersectionObserver(([entry]) => {
    visible = entry.isIntersecting
  }).observe(canvas)

  const frame = (ms) => {
    if (visible && !document.hidden) draw(gl, ms / 1000) // your draw call
    if (!still) requestAnimationFrame(frame)
  }
  requestAnimationFrame(frame)
}
```

De vangrails in een paar regels: een terugvaloptie wanneer WebGL ontbreekt, maar één frame bij verminderde beweging, en niet tekenen buiten beeld of in een verborgen tabblad.

Ter referentie: de [WebGL API op MDN](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API) documenteert elke aanroep die hier wordt gebruikt, en [caniuse](https://caniuse.com/webgl2) houdt de ondersteuning voor WebGL2 in browsers bij.

## Een shader-kleurverloop ontwerpen: wat je brieft

Je kunt een shader sturen zonder GLSL te lezen. Brief hem zoals je een materiaal zou briefen, niet een kleurstaal. Geef de grondkleur en de drie tot vijf kleuren die erop leven, in hex. Beschrijf de structuur: zachte poelen, lange banden, gevouwen zijde. Noem de snelheid in seconden per cyclus in plaats van ‘langzaam’, want langzaam betekent voor iedereen iets anders. Zeg hoeveel korrel, en of hij beweegt. Zeg tot slot waar tekst komt te staan, zodat het drukste deel van het vlak daar wegblijft.

> **Beoordeel het eerst stilstaand** Pauzeer de shader op een willekeurig frame en bekijk het als een statisch beeld. Ziet een stilstaand frame er niet goed uit, dan redt de beweging het niet. Beweging hoort iets toe te voegen aan een ontwerp dat al werkt.

## Hoe Gradiently Marks tekent met shaders

De Marks van Gradiently zijn shader-kleurverlopen. Elke Mark is een recept van kleuren, tot drie gestapelde materialen, licht, korrel en beweging, en de Studio tekent hem live met WebGL2 achter wat je ook ontwerpt. Omdat hij wordt berekend in plaats van opgeslagen, rendert dezelfde Mark scherp op elk van de platformformaten en beweegt hij wanneer je een loopende video exporteert. De renderer weet ook waar je woorden staan, dus hij kan het rustigste deel van het vlak erachter schuiven. [Wat is een Mark](https://gradiently.design/nl/guide/what-is-a-mark) legt het recept volledig uit.

Een Mark is een shaderrecept met een naam, een code en één eigenaar. Op zijn pagina zie je hem live renderen.

## FAQ

### Wat is een WebGL-kleurverloop?

Het is een kleurverloop dat wordt getekend door een shader, een klein programma op de grafische chip dat de kleur van elke pixel berekent. Omdat het een formule is, kan het bewegen, korrel toevoegen en op elk formaat renderen.

### Is een shader-kleurverloop slecht voor de prestaties?

Een stilstaande of gepauzeerde shader kost weinig, maar een animerende houdt de GPU bezig zolang hij in beeld is. Pauzeer hem buiten beeld, teken op een verstandige resolutie en houd een CSS-terugvaloptie.

### Kan ik een shader-kleurverloop maken zonder te programmeren?

Ja. Verschillende ontwerptools genereren shaderachtergronden met regelaars, en de Forge van Gradiently bouwt Marks, die shader-kleurverlopen zijn, uit een zin of regelaar voor regelaar.

### Houden shader-kleurverlopen rekening met instellingen voor verminderde beweging?

Alleen als ze daarvoor zijn gebouwd. Controleer de media query prefers-reduced-motion in JavaScript en teken één stilstaand frame wanneer ze is ingesteld.

### Wat is GLSL?

GLSL is de shadertaal die WebGL gebruikt. Fragment shaders die erin zijn geschreven bepalen de kleur van elke pixel, en de browser compileert ze voor de grafische chip.
