A glowing monitor displaying colorful animated shader effects in a softly lit studio, with a gray-blue cat watching from a nearby chair.

Shaders: GPU Eye-Candy Without Writing a Single Line of WGSL

The Gray Cat
The Gray Cat
0 views

Shader programming has a reputation, and it is not a friendly one. To paint a moving gradient or a film-grain overlay the "real" way, you write GLSL or WGSL by hand, juggle uniforms, manage a render loop, and pray your math behaves the same on every GPU. Shaders (published on npm as shaders) takes that whole intimidating pipeline and hides it behind something you already know how to use: components with props.

Built by Shader Effects, Inc. and available at shaders.com, it bills itself as "the component system for creative WebGPU effects in the browser." Instead of authoring shaders, you compose them. You write <MeshGradient /> or <Halftone /> the same way you write a <div>, and the library compiles your tree down to GPU shaders rendered on a canvas. It ships wrappers for React/Next, Vue/Nuxt, Svelte, Solid and vanilla JS, so the same catalog of effects follows you across frameworks. This article focuses on the React side, but the mental model is identical everywhere.

Why Reach for It

The pitch is breadth plus ergonomics. Shaders ships over a hundred effects — 119 in the current registry — organized into nine categories: Blurs, Stylize, Textures, Distortions, Adjustments, Interactive, Shapes, Shape Effects and Utilities. That covers an enormous amount of creative ground from a single dependency:

  • GradientsMeshGradient, LinearGradient, RadialGradient, ConicGradient, FlowingGradient, MultiPointGradient and more.
  • Post-processing and stylizeHalftone, Dither, Ascii, Pixelate, Duotone, FilmGrain, Glitch, CRTScreen, VHS.
  • BlursBlur, ZoomBlur, ProgressiveBlur, TiltShift, AngularBlur, and several others.
  • DistortionsBulge, Twirl, Ripples, Liquify, WaveDistortion, Kaleidoscope.
  • Textures and noiseVoronoi, SimplexNoise, FractalNoise, Truchet, HexGrid, MarbleTexture.
  • Interactive sourcesCursorTrail, CursorRipples, FlowField, FloatingParticles.
  • Media sourcesImageTexture, VideoTexture, WebcamTexture, DOMTexture.

Because every effect is a real component, you compose and stack them like any other UI. Each one takes plain, typed, documented props with sensible defaults, so a designer can dial in a look without ever touching shader code. And under the hood it leans on three.js and its node-based TSL system to target WebGPU, which is where the "modern" in "modern frontends" earns its keep.

A Quick Note Before You Install

Shaders is not open source. It ships under the Shader Effects License Agreement, which lets you use it freely for personal and production work, but prohibits redistribution, building competing tools, SaaS use without an OEM agreement, and republishing exported code as a public library. That is a meaningful difference from the MIT-licensed alternatives in this space, so if you are building a product around it, read Section 4 of the license first. It is a great tool; just go in with eyes open.

Getting It Into Your Project

Shaders is a single package with one runtime dependency (three) and a set of optional peer dependencies — you only need the one matching your framework. For React, that means react and react-dom.

npm install shaders

Or with yarn:

yarn add shaders

The package is ESM-only and marked sideEffects: false, and every framework lives behind its own subpath (shaders/react, shaders/vue, shaders/svelte, shaders/solid, shaders/js). The full install is large because it bundles all 119 effects across every framework, but thanks to tree-shaking and subpath imports, only the components you actually reference end up in your bundle.

Painting Your First Effect

Every Shaders scene starts with the root <Shader> component. It spins up the renderer, wires the canvas, and provides context to the effect nodes nested inside it. Import it and any effects you want from shaders/react:

import { Shader, MeshGradient } from 'shaders/react';

export function Hero() {
  return (
    <Shader style={{ width: '100%', height: 400 }}>
      <MeshGradient />
    </Shader>
  );
}

That is genuinely it. The <Shader> wrapper accepts a style and className so you size and position it like any DOM element, and the <MeshGradient /> child renders an animated, GPU-driven gradient inside. No render loop, no uniforms, no canvas refs.

The root component also exposes a handful of rendering controls. Two of the most useful are colorSpace, which accepts "p3-linear" (the default) or "srgb", and toneMapping, which accepts a film-style curve like "linear", "aces", "agx", "reinhard", "cineon", "neutral", "hable" or "unreal". There is also an onReady callback that fires once the renderer is live:

<Shader
  colorSpace="srgb"
  toneMapping="aces"
  onReady={() => console.log('GPU is warmed up')}
>
  <MeshGradient />
</Shader>

Configuring an Effect With Props

Every effect is configured the same way: through props, each with a documented type and a default. Take the Dither effect, which turns smooth gradients into stylized ordered-dither patterns. Its component-specific props include a pattern (one of "bayer2", "bayer4", "bayer8", "clusteredDot", "blueNoise" or "whiteNoise"), a pixelSize between 1 and 20, a threshold from 0 to 1, and a spread controlling how much of the luminance range participates:

import { Shader, Dither } from 'shaders/react';

export function RetroPanel() {
  return (
    <Shader style={{ width: 480, height: 480 }}>
      <Dither pattern="bayer8" pixelSize={6} threshold={0.5} spread={0.8} />
    </Shader>
  );
}

Beyond their own properties, every effect shares a common set of base props inherited from the underlying shader node. These let you control how an effect behaves and composites:

  • opacity — fade the effect in or out.
  • blendMode — choose how it blends with whatever sits behind it.
  • visible — toggle rendering on and off.
  • renderOrder — control stacking order within the scene.
  • transform — a partial transform config for position, scale and rotation.
  • maskSource and maskType — mask the effect to a shape or region.

Because these are universal, once you learn them for one component you know them for all 119.

Stacking Effects Like Layers

The real fun starts when you nest effects. A parent effect can act as a source — a texture, say — and its children layer on top, each compositing with blendMode and opacity. This lets you build up a rich look the same way you would stack adjustment layers in an image editor:

import { Shader, ImageTexture, Halftone, FilmGrain } from 'shaders/react';

export function PrintMagazineCover() {
  return (
    <Shader style={{ width: 600, height: 800 }}>
      <ImageTexture src="/cover.jpg">
        <Halftone />
        <FilmGrain opacity={0.4} blendMode="overlay" />
      </ImageTexture>
    </Shader>
  );
}

Here an image is loaded as a GPU texture, a halftone pass gives it that newsprint feel, and a subtle film-grain layer is blended on top at 40% opacity. Swapping the look is a matter of reordering or replacing children — no shader rewrites, no manual framebuffer wrangling. The Group utility component is handy for organizing several effects that should be treated as one unit.

Building Galleries With the Registry

If you are building something more dynamic — an effect picker, a live gallery, your own little design tool — Shaders exposes its catalog through a registry API on the root entry point. You can list everything, fetch a single effect's metadata, filter by category, or enumerate the categories themselves:

import {
  getAllShaders,
  getShadersByCategory,
  getCategories,
} from 'shaders';

const categories = getCategories();
// ['Blurs', 'Stylize', 'Textures', 'Distortions', 'Adjustments',
//  'Interactive', 'Shapes', 'Shape Effects', 'Utilities']

const distortions = getShadersByCategory('Distortions');
const everything = getAllShaders();
// each entry: { name, category, description?, fileName, requiresChild?, ... }

Each registry entry carries a name, category, optional description, and flags like requiresChild (true for effects that need a source nested inside them). That is enough to drive a UI that renders a thumbnail per effect, groups them by category, and knows which ones need a child source — all without hard-coding the list of 119 components yourself.

Generating Code From Presets

Shaders also ships a code-generation layer, which is the same machinery the hosted Design Editor on shaders.com uses to export components. Each framework has a /codegen subpath, and the React one exposes generatePresetCode. You hand it a preset describing a tree of components, and it hands back ready-to-paste source:

import { generatePresetCode } from 'shaders/react/codegen';

const code = generatePresetCode(
  {
    components: [
      {
        type: 'ImageTexture',
        props: { src: '/cover.jpg' },
        children: [{ type: 'Halftone', props: {} }],
      },
    ],
  },
  'p3-linear',
  'aces',
);

This closes a nice loop: design a look visually in the editor, export it as a preset, and either drop the generated code into your app or generate it on the fly. The same colorSpace and toneMapping options you pass to the live <Shader> root are available here as the second and third arguments, so what you preview is what you ship.

A Couple of Practical Notes

Two things are worth keeping in your back pocket. First, the library includes built-in telemetry that runs by default; if that does not suit your project, the root component takes a disableTelemetry prop to turn it off. Second, because everything renders through three.js's WebGPU pipeline, you will get the best results in WebGPU-capable browsers — plan your fallbacks accordingly for older targets.

The Verdict

Shaders is one of those libraries that quietly expands what feels possible on a weekday afternoon. The barrier to GPU effects has always been the shader code itself, and by turning that code into a catalog of configurable components, Shaders erases it almost entirely. You compose <MeshGradient />, <Halftone /> and <Liquify /> the way you compose buttons and cards, stack them with blend modes and opacity, and lean on a registry and code generator when you want to get fancy.

The trade-offs are real and worth weighing: a proprietary license, default-on telemetry, a hefty install footprint, and a hard dependency on modern GPU rendering. But if you are building marketing pages, hero sections, creative UI, or image and video post-effects — and you would rather ship something striking today than spend a week debugging WGSL — Shaders makes a compelling case. Sometimes the most impressive magic is the kind that fits in a single component tag.