npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

webgpu-video-shaders

v0.1.0

Published

Video processing algorithms as WGSL shader generators — libplacebo ports + originals

Readme

webgpu-video-shaders

libplacebo's video processing algorithms as WGSL shader generators. Zero dependencies. Pure TypeScript. Works with any WebGPU pipeline.

This is a faithful 1:1 port of libplacebo (the rendering engine behind mpv) to WebGPU/WGSL. Every public shader function is ported, every algorithm matches the original C code.

Install

npm install webgpu-video-shaders

Quick Start

import { createDeband } from 'webgpu-video-shaders/libplacebo';

const deband = createDeband({ iterations: 2, threshold: 4, grain: 6 });

// deband.fn contains a WGSL function: fn deband(tex, coord, dims) -> vec4f
// Paste it into your shader and call it:
const shader = /* wgsl */ `
  @group(0) @binding(0) var inputTex: texture_2d<f32>;
  @group(0) @binding(1) var outputTex: texture_storage_2d<rgba8unorm, write>;

  ${deband.fn}

  @compute @workgroup_size(16, 16)
  fn main(@builtin(global_invocation_id) id: vec3u) {
    let dims = textureDimensions(inputTex);
    if (id.x >= dims.x || id.y >= dims.y) { return; }
    let result = deband(inputTex, vec2i(id.xy), vec2i(dims));
    textureStore(outputTex, vec2i(id.xy), result);
  }
`;

Imports

Import from specific barrels for tree shaking:

// libplacebo ports (faithful 1:1 translations)
import { createDeband, createToneMap } from 'webgpu-video-shaders/libplacebo';

// Original implementations (not from libplacebo)
import { createVignette, createSharpen } from 'webgpu-video-shaders/original';

// Shared types
import type { ShaderFunction, ShaderResult } from 'webgpu-video-shaders/core';

// Or import everything (no tree shaking)
import { createDeband, createVignette } from 'webgpu-video-shaders';

Two-Tier API

Tier 1: Composable Functions

Returns a WGSL fn string. You wire it into your own shader with your own bindings. Works with any WebGPU pipeline.

import { createDeband, createToneMap, createGrain } from 'webgpu-video-shaders/libplacebo';

const deband = createDeband({ iterations: 1, threshold: 3 });
const tonemap = createToneMap({ method: 'hable', srcPeakNits: 1000 });
const grain = createGrain({ amount: 4 });

// Compose multiple effects in a single shader (one dispatch, no intermediate textures):
const shader = /* wgsl */ `
  @group(0) @binding(0) var inputTex: texture_2d<f32>;
  @group(0) @binding(1) var outputTex: texture_storage_2d<rgba16float, write>;

  ${deband.fn}
  ${tonemap.fn}
  ${grain.fn}

  @compute @workgroup_size(16, 16)
  fn main(@builtin(global_invocation_id) id: vec3u) {
    let dims = textureDimensions(inputTex);
    if (id.x >= dims.x || id.y >= dims.y) { return; }
    let coord = vec2i(id.xy);

    var color = ${deband.fnName}(inputTex, coord, vec2i(dims));
    color = ${tonemap.fnName}(color);
    color = ${grain.fnName}(color, coord);

    textureStore(outputTex, coord, color);
  }
`;

Tier 2: Complete Shaders

Returns a full WGSL compute shader string ready for device.createShaderModule(). Includes bindings, workgroup size, everything.

import { createDebandShader } from 'webgpu-video-shaders/libplacebo';

const { wgsl, extraBindings, description } = createDebandShader({
  iterations: 2, threshold: 4, grain: 6
});

const module = device.createShaderModule({ code: wgsl });

Usage with Browser WebGPU

import { createToneMap, createDeband } from 'webgpu-video-shaders/libplacebo';

const deband = createDeband({ iterations: 1 });
const tonemap = createToneMap({ method: 'bt2390', srcPeakNits: 1000, dstPeakNits: 203 });

const shaderCode = /* wgsl */ `
  @group(0) @binding(0) var inputTex: texture_2d<f32>;
  @group(0) @binding(1) var outputTex: texture_storage_2d<rgba16float, write>;

  ${deband.fn}
  ${tonemap.fn}

  @compute @workgroup_size(16, 16)
  fn main(@builtin(global_invocation_id) id: vec3u) {
    let dims = textureDimensions(inputTex);
    if (id.x >= dims.x || id.y >= dims.y) { return; }
    let coord = vec2i(id.xy);
    var color = ${deband.fnName}(inputTex, coord, vec2i(dims));
    color = ${tonemap.fnName}(color);
    textureStore(outputTex, coord, color);
  }
`;

// Standard WebGPU pipeline setup
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();

const pipeline = device.createComputePipeline({
  layout: 'auto',
  compute: { module: device.createShaderModule({ code: shaderCode }), entryPoint: 'main' },
});

// Create textures, bind group, dispatch...
const bindGroup = device.createBindGroup({
  layout: pipeline.getBindGroupLayout(0),
  entries: [
    { binding: 0, resource: inputTexture.createView() },
    { binding: 1, resource: outputTexture.createView() },
  ],
});

const encoder = device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatchWorkgroups(Math.ceil(width / 16), Math.ceil(height / 16));
pass.end();
device.queue.submit([encoder.finish()]);

Usage with TypeGPU

import { createDeband } from 'webgpu-video-shaders/libplacebo';
import tgpu from 'typegpu';

const root = await tgpu.init();

const deband = createDeband({ iterations: 2, threshold: 4 });

// TypeGPU supports raw WGSL via tgpu.resolve
const shaderCode = /* wgsl */ `
  @group(0) @binding(0) var inputTex: texture_2d<f32>;
  @group(0) @binding(1) var outputTex: texture_storage_2d<rgba8unorm, write>;

  ${deband.fn}

  @compute @workgroup_size(16, 16)
  fn main(@builtin(global_invocation_id) id: vec3u) {
    let dims = textureDimensions(inputTex);
    if (id.x >= dims.x || id.y >= dims.y) { return; }
    let result = ${deband.fnName}(inputTex, vec2i(id.xy), vec2i(dims));
    textureStore(outputTex, vec2i(id.xy), result);
  }
`;

const module = root.device.createShaderModule({ code: shaderCode });

Usage with React Native Skia (useVideo)

import { useVideo } from '@shopify/react-native-skia';
import { createConeDistort } from 'webgpu-video-shaders/libplacebo';

// Generate shader at module scope (runs once)
const cone = createConeDistort({ type: 'deuteranopia', severity: 1.0 });
const COLORBLIND_SHADER = /* wgsl */ `
  @group(0) @binding(0) var inputTex: texture_2d<f32>;
  @group(0) @binding(1) var outputTex: texture_storage_2d<rgba16float, write>;
  ${cone.fn}
  @compute @workgroup_size(16, 16)
  fn main(@builtin(global_invocation_id) id: vec3u) {
    let dims = textureDimensions(inputTex);
    if (id.x >= dims.x || id.y >= dims.y) { return; }
    let color = textureLoad(inputTex, vec2i(id.xy), 0);
    textureStore(outputTex, vec2i(id.xy), ${cone.fnName}(color));
  }
`;

// In your component, useVideo provides frames as SkImage textures.
// With Skia Graphite (SK_GRAPHITE=1), navigator.gpu is available via Dawn,
// and you can run compute shaders on the GPU texture backing the SkImage.
function VideoPlayer() {
  const { currentFrame } = useVideo('video.mp4');
  // ... render currentFrame via <Canvas> + <Image>, process via navigator.gpu
}

LUT-Based HDR Pipeline

For production HDR tone mapping, use the LUT-based pipeline that matches libplacebo's architecture:

import {
  createPeakDetectShader,
  createToneMapLutGenPipelineShader,
  createToneMapLutApplyShader,
} from 'webgpu-video-shaders/libplacebo';

// Pass 1: Peak detection (measures scene luminance)
const peakDetect = createPeakDetectShader({ histogramBins: 64 });

// Pass 2: LUT generation (reads peak stats, generates/caches 1D tone map LUT)
const lutGen = createToneMapLutGenPipelineShader({
  method: 'bt2390',
  dstPeakNits: 203,
});

// Pass 3: LUT application (reads LUT, applies per-pixel in IPT space)
const lutApply = createToneMapLutApplyShader({
  lutSize: 256,
  srcPeakNits: 1000,
  srcTransfer: 'hlg',        // Apple Log → linear
  dstTransfer: 'srgb',       // → sRGB for display
  srcPrimaries: 'bt2020',    // BT.2020 → BT.709
  dstPrimaries: 'bt709',
});

// The LUT gen shader caches the peak value — if scene brightness hasn't
// changed since last frame, LUT regeneration is skipped automatically.

What's Included

libplacebo Ports (webgpu-video-shaders/libplacebo)

Every public shader function from libplacebo is ported:

Color Pipeline

  • createColorMap — linearize/delinearize (17 transfer functions: sRGB, PQ, HLG, BT.1886, gamma variants, V-Log, S-Log, ProPhoto, ST428, scRGB) + gamut conversion with Bradford chromatic adaptation
  • createColorMapShader — full pl_shader_color_map_ex orchestrator (linearize → IPT → tone map → gamut map → delinearize with contrast recovery and clipping diagnostics)
  • createDecodeColor / createEncodeColor — YCbCr decode/encode (BT.601/709/2020, BT.2020-CL, ICtCp PQ/HLG, Dolby Vision, XYZ, chroma location, subsampling)
  • createDoviReshape — Dolby Vision polynomial + MMR reshaping
  • createConeDistort — color vision deficiency simulation (8 types, CAT16 matrix)
  • createAlpha — premultiply/unpremultiply
  • createSigmoidize / createUnsigmoidize — anti-ringing sigmoid for upscaling

Tone Mapping (12 curves)

  • createToneMap — clip, bt2390, bt2446a, st2094_40, st2094_10, spline, reinhard, mobius, hable, gamma, linear, linear_light
  • createToneMapLutGenShader / createToneMapLutApplyShader — GPU LUT-based pipeline with caching

Gamut Mapping (10 methods)

  • createGamutMap — clip, perceptual, softclip, relative, saturation, absolute, desaturate, darken, highlight, linear (operates in IPTPQc4 space)
  • createGamutMapLutGenShader / createGamutMapLutApplyShader — 3D LUT pipeline

Peak Detection

  • createPeakDetectShader — 12-slice sharding, PQ-biased histogram, IIR temporal smoothing, scene-change detection, percentile-based peak measurement

Sampling

  • createUpscale — nearest, bilinear, bicubic, hermite, gaussian
  • createPolarSampleShader — EWA sampling (ewa_lanczos, ewa_jinc, etc.)
  • createOrthoSampleShader — separable 2-pass scaling
  • createOversample — spatial oversampling
  • createFilterKernel — 21 filter weight functions (sinc, jinc, kaiser, mitchell, etc.)
  • createDistortLpShader — affine geometric distortion

Dithering

  • createDither — ordered (16×16), white noise, blue noise (LUT), ordered LUT, with temporal rotation and gamma-corrected path
  • createErrorDiffusionShader — GPU error diffusion (Floyd-Steinberg, Sierra Lite, Burkes, Stucki, Atkinson, Sierra 2/3)
  • generateBayerMatrix / generateBlueNoise — CPU-side LUT generators

Other

  • createDeband — flash3kyuu debanding with PCG3D PRNG
  • createGrain — film grain with vec3 independent PRNG
  • createExtractFeaturesShader — IPT intensity extraction for contrast recovery

Original (webgpu-video-shaders/original)

  • createLanczos — windowed sinc resampling with anti-ringing
  • createGaussianBlur — precomputed kernel blur
  • createSharpen — unsharp mask / CAS adaptive sharpening
  • createVignette — optical vignetting
  • createDistort — barrel/pincushion/chromatic aberration
  • createDeinterlace — bob/weave/yadif

Source

All libplacebo ports reference the original C source by file and line number. The source is haasn/libplacebo (LGPL-2.1+).

License

LGPL-2.1-or-later (matching libplacebo)