Shaders and visual effects

Play this lesson

Nova should glow

Remy has a wish list for Hyper Shards. Nova, the space crystal, should glow and pulse. Aliens should flash white when they are hit. And the whole game should look like an old arcade screen, with thin dark lines across it.

Drawing every version by hand would mean hundreds of extra pictures. Instead, games change pixels on the fly with small programs called shaders. This lesson shows what they are, how PixiJS makes them easy, and what they cost.

Corners and pixels

The GPU is the graphics chip: thousands of small workers running at once. It draws everything as triangles. A sprite is a rectangle made of two triangles, with four corners called vertices.

A full-screen effect on a 1,920 by 1,080 screen runs about 2 million times per frame. At 60 FPS that is over 124 million times a second.

Quick check

Nova's glow needs a different colour for each pixel around the crystal. Which shader decides that?

  1. The fragment shader, which runs once for each pixel
  2. The vertex shader, which runs once for each corner
  3. Neither: the game loop's input step does it
Show the answer

The fragment shader, which runs once for each pixel

Colour per pixel is the fragment shader's job. The vertex shader only places the corners, and a sprite has just four of them.

Tint is multiplying

The cheapest colour effect needs no shader code from us. A PixiJS sprite has a tint, which multiplies every pixel's colour by one colour. sprite.tint = 0xff0000 keeps only the red. 0xffffff, white, means no tint at all.

Inside the GPU, each colour channel (red, green, blue) is a number from 0 to 1, and multiplying by a number up to 1 can only keep it the same or make it smaller. So tint can darken or colour a sprite, but never brighten it. A white hit flash needs a tiny fragment shader that mixes each colour toward white.

Read the code

flash.frag
varying vec2 vTextureCoord;uniform sampler2D uTexture;uniform float uFlash; // 0 = normal, 1 = white void main() {  vec4 c = texture2D(uTexture, vTextureCoord);  vec3 white = vec3(c.a); // white where solid  gl_FragColor = vec4(mix(c.rgb, white, uFlash), c.a);}

mix(a, b, t) goes part of the way from a to b: t = 0 gives a, t = 1 gives b. A solid red alien pixel is (1, 0, 0). What does it become when uFlash is 0.5?

  1. (1, 0.5, 0.5): pink, halfway to white
  2. (1, 0, 0): still red
  3. (1, 1, 1): pure white
  4. (0.5, 0, 0): a darker red
Show the answer

(1, 0.5, 0.5): pink, halfway to white

Each channel moves halfway toward 1. Red stays 1, and green and blue go from 0 to 0.5, which is pink. The game sets uFlash to 1 on a hit and fades it back to 0 over a few frames, so the alien flashes white and recovers.

Filters: the easy door

In PixiJS, a filter is a ready-made effect built on shaders. You attach it to a sprite or a container:

nova.filters = [new BlurFilter({ strength: 8 })];

PixiJS v8 comes with BlurFilter, ColorMatrixFilter, NoiseFilter, DisplacementFilter and AlphaFilter. The community package pixi-filters (version 6 goes with PixiJS v8) adds dozens more, like GlowFilter, CRTFilter, ShockwaveFilter and ColorOverlayFilter. Filters in a list run in order, and you can write your own from a Filter and your shader code.

The Hyper Shards effects list

Bolt says: Sparks and particles are in the juice lesson. For thousands of them, PixiJS has a ParticleContainer. Its particles have no filters or children, which keeps them fast.

Player message

Kai, in the studio chat
I set the hit alien's tint to 0xffffff so it flashes white. Nothing happens. Is PixiJS broken?

What is the best reply to Kai?

  1. Tint multiplies, and multiplying by white changes nothing. Use the flash shader or a ColorOverlayFilter
  2. Use 0x000000 instead, which is the code for white
  3. Tint only works on text, not on sprites
Show the answer

Tint multiplies, and multiplying by white changes nothing. Use the flash shader or a ColorOverlayFilter

White, 0xffffff, is the no-tint value, because every channel times 1 stays the same. Tint can never brighten. Mixing toward white in a fragment shader, or an overlay filter, can.

Effects cost frames

PixiJS's own performance tips warn that filters are expensive, and too many will slow things down. A filter on the whole screen runs for millions of pixels every frame, and a phone's GPU is far smaller than a gaming PC's.

Effects with an off switch

The accessibility lesson explains why flashing can be dangerous for people with photosensitive epilepsy. The web's rule, WCAG 2.3.1, is precise: nothing should flash more than three times in any one second, unless the flashes are small or dim enough to stay under set limits.

Shaders make that easy to break by accident. Flashing the whole screen on every hit in a busy wave can mean ten flashes a second. So big flashes are capped at three a second, and every effect reads the settings: flashes on or off, and screen effects like the CRT lines on or off.

Bolt says: Next lesson: tutorials and menus, the first things every new player touches.

Fact-checked 9 October 2026