Shaders and visual effects
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 vertex shader runs once for every corner and works out where it lands on the screen.
- A fragment shader runs once for every pixel the shape covers and decides its colour. A fragment is a pixel being drawn.
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?
- The fragment shader, which runs once for each pixel
- The vertex shader, which runs once for each corner
- 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
1varying vec2 vTextureCoord;2uniform sampler2D uTexture;3uniform float uFlash; // 0 = normal, 1 = white4 5void main() {6 vec4 c = texture2D(uTexture, vTextureCoord);7 vec3 white = vec3(c.a); // white where solid8 gl_FragColor = vec4(mix(c.rgb, white, uFlash), c.a);9}
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, 0.5, 0.5): pink, halfway to white
- (1, 0, 0): still red
- (1, 1, 1): pure white
- (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
- GlowA GlowFilter on Nova, with its strength rising and falling a little each frame, so the crystal pulses.
- Hit flashThe flash shader on each alien: uFlash jumps to 1 on a hit, then fades back to 0 in about a tenth of a second.
- Old arcade screenOne CRTFilter over the game: thin lines, a slight curve, a little noise and darker edges.
- Scrolling starsNo shader needed. A TilingSprite repeats a star picture, and moving its tilePosition each frame scrolls it forever.
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
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?
- Tint multiplies, and multiplying by white changes nothing. Use the flash shader or a ColorOverlayFilter
- Use 0x000000 instead, which is the code for white
- 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.
- Put filters on the few things that need them, not on everything.
- If you know how big an effect is, set its
filterArea, so PixiJS skips measuring it every frame. - Turn an effect off with
filters = null, which also frees memory. - Measure on a cheap phone, and offer an Effects: high or low setting.
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
- PixiJS: filters pixijs.com
- PixiJS: performance tips pixijs.com
- PixiJS: scene objects (tint) pixijs.com
- PixiJS: TilingSprite pixijs.com
- PixiJS: ParticleContainer pixijs.com
- pixi-filters github.com
- MDN: adding 2D content to a WebGL context developer.mozilla.org
- WCAG 2.2: three flashes or below threshold w3.org