1

Fragment Shader Basics

A .frag file is a fragment shader that alters pixel colors to create visual effects. It receives texture coordinates, samples the texture, and outputs modified colors via gl_FragColor.

Code: GLSL Fragment Shader Structure
glsl
uniform sampler2D uSampler;      // texture sampler (built-in pixi.js)
varying vec2 vTextureCoord;      // texture coordinates (built-in)

void main(void) {
    vec4 color = texture2D(uSampler, vTextureCoord); // sample texture
    gl_FragColor = color;    // output color
}
Result: Shader Pipeline Overview
Live Interactive
🎨

Input: Texture

The original image or video frame is passed as a sampler2D uniform named uSampler

⚙️

Process: Shader

GPU runs your GLSL code per pixel, reading from vTextureCoord and applying math

🎬

Output: FragColor

Each pixel's final color is written to gl_FragColor, producing the visual effect

2

Uniform Variables & Dynamic Control

Shader effects are made dynamic through uniform variables. Different input values produce different visual changes. Common types include float, vec2 (2D vector), and vec4.

Code: Jitter Shader with Uniforms
glsl
precision highp float;

varying highp vec2 vTextureCoord;
uniform sampler2D uSampler;
uniform float scale;           // zoom factor
uniform float horzIntensity;  // horizontal jitter
uniform float vertIntensity;  // vertical jitter

void main() {
    // Scale texture coordinates
    vec2 uv = vec2(
        (scale - 1.0) * 0.5 + vTextureCoord.x / scale,
        (scale - 1.0) * 0.5 + vTextureCoord.y / scale
    );
    vec4 tex = texture2D(uSampler, uv);

    // Channel-shift for chromatic jitter
    vec4 shift1 = texture2D(uSampler, uv + vec2(
        -0.05*(scale-1.0)*horzIntensity*2.,
        -0.05*(scale-1.0)*vertIntensity*2.));
    vec4 shift2 = texture2D(uSampler, uv + vec2(
        -0.1*(scale-1.0)*horzIntensity*2.,
        -0.1*(scale-1.0)*vertIntensity*2.));

    // RGB channel blend
    vec3 blend = vec3(tex.r, tex.g, shift1.b);
    vec3 result = vec3(shift2.r, blend.g, blend.b);
    gl_FragColor = vec4(result, tex.a);
}
Result: Interactive Jitter Effect
Live Interactive — Chromatic Jitter
CapCut Effect
Scale 1.00
Horz 0.00
Vert 0.00
3

Applying Filters with pixi.js

Use PIXI.Filter to wrap a fragment shader and apply it to any display object. Uniform values are updated each frame to create animation.

Code: Creating & Animating a Filter
javascript
const uniforms = {
    scale: 1.0,
    horzIntensity: 0.5,
    vertIntensity: 0.5,
};

// Frame-by-frame scale values (low-cost animation)
const scale = [1.0,1.07,1.1,1.13,1.17,1.2,1.2,1.0,
             1.0,1.0,1.0,1.0,1.0,1.0,1.0];

// Create a custom filter from frag source
const filter = new PIXI.Filter(null, frag, uniforms);
container.filters = [filter];

// Animate by updating uniforms each frame
let start = 0;
app.ticker.add(() => {
    filter.uniforms.scale = scale[start++ % scale.length];
});
Result: Scale Animation Timeline
Live Interactive — Frame Animation
Frame
0
Scale
1.00
FPS
--
4

Advanced Uniforms: Blur Effect

Shaders support various uniform types: float, vec2 (2D vector), and vec4 (4D vector). The blur effect below uses multiple vec2 uniforms for per-channel directional blur.

Code: Blur Shader with vec2 Uniforms
javascript
const uniforms = {
    blurSize: 0.0,
    angleR: 0.0,
    angleG: 0.0,
    angleB: 0.0,
    moveR: [0, 0],       // vec2 — red channel blur direction
    moveG: [0, 0],       // vec2 — green channel blur direction
    moveB: [0, 0],       // vec2 — blue channel blur direction
    // vec4 — [x, y, viewWidth, viewHeight]
    u_ScreenParams: [0, 0, viewWidth, viewHeight]
};
Result: CSS Blur Simulation
Live Interactive — Blur Effect
Blur demo
Blur Size 0.0
Angle R
Angle G
Angle B
5

Atmospheric Effects: Screen Blending

The screen blending mode is ideal for atmospheric effects like rain, fog, and snow. Black stays black, white stays white, and other colors become lighter—perfect for overlays on dark or transparent backgrounds.

Code: Screen Blend Characteristics
javascript
// Screen blending formula per channel:
// result = 1 - (1 - base) * (1 - blend)
//
// Key properties:
// - Any color screened with BLACK = original color
// - Any color screened with WHITE = white
// - Any color screened with another = lighter
//
// In CSS: mix-blend-mode: screen;
//
// In pixi.js:
const sprite = new PIXI.Sprite(texture);
sprite.blendMode = PIXI.BLEND_MODES.SCREEN;
Result: Atmospheric Effect Showcase
Live Interactive — Click to Toggle Effects
mix-blend-mode: screen
6

Video Decoding with WebCodecs

Rather than loading hundreds of PNG frames, use the WebCodecs API to decode MP4 videos directly in the browser. This is far more efficient—one HTTP request instead of hundreds, and files are ~10x smaller than APNG.

Code: WebCodecs Video Frame Pipeline
javascript
// Using mp4box.js + WebCodecs to decode MP4
const mp4box = MP4Box.createFile();
mp4box.onReady = async (info) => {
    const track = info.videoTracks[0];
    const decoder = new VideoDecoder({
        output(frame) {
            // Draw each decoded frame to canvas
            ctx.drawImage(frame, 0, 0, width, height);
            frame.close();
        },
        error(e) { console.error(e); }
    });
    // ... configure and decode
};

// Fetch and append MP4 data
const res = await fetch('./lightning.mp4');
const buffer = await res.arrayBuffer();
mp4box.appendBuffer(buffer);
Result: Why Video Over Image Sequences?
Comparison Table
Format File Size HTTP Requests Decode Speed
📷 PNG Sequence (100 frames) ~50 MB 100 Fast
🖼️ APNG (animated) ~30 MB 1 Average
🎬 MP4 + WebCodecs ~3 MB 1 Very Fast
7

Color Map (LUT) Filters

Video filters like those in CapCut often use ColorMapFilter—a technique that maps source colors through a lookup table (LUT) image. The result is a consistent, film-like color grade applied to every frame. LUT files come in formats like .cube or .3dl.

Code: Color Mapping Concept
javascript
// Algorithmic filter (e.g., Gaussian blur, inversion)
// → Mathematical computation, sometimes lacks refinement
ctx.filter = 'blur(4px) hue-rotate(90deg)';

// ColorMap filter (LUT-based)
// → Color mapping via a lookup table image
// → More refined, film-quality color grading
// File formats: .cube, .3dl, or ColorMap PNG
//
// In pixi.js:
const colorMapFilter = new PIXI.ColorMapFilter(
    PIXI.Texture.from('./lut.png'),
    PIXI.FORMATS.CUBE
);
container.filters = [colorMapFilter];
Result: CSS Filter Comparison
Live Interactive — Click Filters to Apply
Filter target
8

Effect Categories Overview

CapCut-style effects fall into two main categories: shader-based effects (frag filters for distortion, blur, jitter) and blended atmospheric effects (screen-blended video overlays for rain, fog, snow).

Result: Category Summary
Two Types of Effects
🎨

Frag Filters

Fragment shaders that modify pixel colors directly

🔄

Distortion

Scale, jitter, wave, swirl via shader math

👁️

Blur

Directional, radial, and per-channel blur effects

💥

Screen Blend

Atmospheric: rain, fog, snow, fireworks

🎬

Video Decode

MP4 via WebCodecs for frame sequences

🖼️

Color Map / LUT

Film-grade color grading via lookup tables