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

glsx

v0.0.3

Published

Composable GLSL shaders for React

Readme

GLSX

Composable GLSL shaders for React.

Installation

npm install glsx
# or
pnpm add glsx
# or
yarn add glsx

Features

  • Write GLSL fragment shaders as React components
  • Compose shaders by nesting them
  • Share global variables across shader components
  • Mix React state with shader uniforms seamlessly

Basic Example

import { Canvas, fragment } from 'glsx'

function App() {
  return (
    <Canvas width={400} height={400}>
      {fragment`
        // Create a simple gradient
        return vec4(uv.x, uv.y, 0.5, 1.0);
      `}
    </Canvas>
  )
}

Advanced Example: Nested Shaders, Global Values, and React State

Deployed version of this example: glsx.vercel.app

This example demonstrates all key features of glsx:

'use client'

import { useState, useEffect, useRef } from 'react'
import { Canvas, fragment, createGlobal } from 'glsx'

// Create a global variable that can be shared across shaders (like React Context)
const distanceFromCenter = createGlobal('float')

// Inner shader component with time-based animation
function ColorEffect() {
  const [time, setTime] = useState(0)
  const speedRef = useRef(1.0)

  // Animate time
  useEffect(() => {
    const interval = setInterval(() => {
      setTime((t) => t + 0.016 * speedRef.current)
    }, 16)
    return () => clearInterval(interval)
  }, [])

  return (
    <>
      {fragment`
        // Read the "context" value calculated by the parent shader
        float dist = ${distanceFromCenter};

        // Create animated wave patterns
        float wave1 = sin(uv.x * 10.0 + ${time}) * 0.5 + 0.5;
        float wave2 = cos(uv.y * 8.0 - ${time} * 0.7) * 0.5 + 0.5;
        float wave3 = sin((uv.x + uv.y) * 6.0 + ${time} * 1.5) * 0.5 + 0.5;

        // Combine waves with context-based modulation
        vec3 color = vec3(
          wave1 * (1.0 - dist * 0.5),
          wave2 * (1.0 - dist * 0.3),
          wave3 * (0.8 + dist * 0.4)
        );

        // Add a pulsing effect based on distance and time
        float pulse = sin(dist * 8.0 - ${time} * 2.0) * 0.2 + 0.8;
        color *= pulse;

        return vec4(color, 1.0);
      `}

      {/* Mix React UI with shaders */}
      <div className="controls">
        <label>
          Animation Speed:
          <input
            type="range"
            min={0}
            max={3}
            step={0.1}
            defaultValue={1.0}
            onChange={(e) => {
              speedRef.current = parseFloat(e.target.value)
            }}
          />
        </label>
      </div>
    </>
  )
}

// Outer shader component
function DistortionEffect() {
  const [distortion, setDistortion] = useState(0.1)

  return (
    <Canvas width={500} height={500}>
      {fragment`
        // Calculate a value based on UV coordinates
        vec2 center = vec2(0.5, 0.5);
        float dist = length(uv - center);

        // Store it in the global variable to pass as "context" to child shaders
        ${distanceFromCenter} = dist;

        // Apply distortion to UV coordinates
        vec2 distortedUV = uv;
        distortedUV.x += sin(uv.y * 10.0) * ${distortion};
        distortedUV.y += cos(uv.x * 10.0) * ${distortion};

        // Update the uv for nested shaders
        uv = distortedUV;

        // Call the nested shader and store its result
        vec4 childColor = ${(<ColorEffect />)};

        // Apply additional processing to the child shader's output
        // Add a vignette effect based on distance from center
        float vignette = 1.0 - (dist * 1.5);
        childColor.rgb *= vignette;

        return childColor;
      `}

      <div className="controls">
        <label>
          Distortion:
          <input
            type="range"
            min={0}
            max={0.5}
            step={0.01}
            value={distortion}
            onChange={(e) => setDistortion(parseFloat(e.target.value))}
          />
        </label>
      </div>
    </Canvas>
  )
}

export default DistortionEffect

What's happening:

  1. Nested Fragment Shaders: DistortionEffect calls ColorEffect using ${(<ColorEffect />)}, allowing shader composition
  2. Shared Global Value as Context: The parent shader calculates distanceFromCenter and stores it in a global variable, which child shaders can read - similar to React Context
  3. Mixed React State and UI: Both components use React's useState to control shader parameters, and render regular React UI elements alongside shader code

API

Canvas

The root component that creates a WebGL2 canvas and renders your shaders.

<Canvas width={400} height={400} className="my-canvas">
  {/* your fragment shader here */}
</Canvas>

fragment

A tagged template literal for writing GLSL fragment shader code.

fragment`
  return vec4(uv.x, uv.y, 0.5, 1.0);
`

Available built-in variables:

  • uv - UV coordinates (vec2, range 0-1)

createGlobal(type)

Creates a global variable that can be shared across shader components.

const myValue = createGlobal('float')  // 'float' | 'int' | 'bool'

sampler2D(src)

Load and use images/textures in your shaders.

fragment`
  vec4 color = texture(${sampler2D('/image.png')}, uv);
  return color;
`

Author

Shu Ding (https://shud.in)

License

ISC