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

react-webgl-fluid-play

v0.8.0

Published

Fluid simulation in React with webgl, pure typescript, no dependencies

Readme

react-webgl-fluid-play

Fluid simulation in React with WebGL, pure TypeScript, and zero runtime dependencies.

npm version npm downloads License: MIT React peer dep Demo


Installation

npm install react-webgl-fluid-play
# or
yarn add react-webgl-fluid-play
# or
pnpm add react-webgl-fluid-play

Requires React 17+ and ReactDOM 17+ as peer dependencies.


Quick start

import Canvas from 'react-webgl-fluid-play';

export default function App() {
  return (
    <div style={{ position: 'fixed', inset: 0 }}>
      <Canvas initialAnimation={{ path: 'oval' }} />
    </div>
  );
}

The component fills 100 % of its parent, so wrap it in a positioned container that defines the size you want.


Public exports

import Canvas, {
  PathFollower,
  PREDEFINED_PATHS,
  PREDEFINED_PATH_IDS,
  cloneFluidPath,
  createPredefinedPath,
  getPredefinedPath,
  getPredefinedPaths,
  isPredefinedPathId,
  type CanvasAnimationOptions,
  type CanvasInitialAnimation,
  type CanvasProps,
  type CanvasRef,
  type FluidPath,
  type FluidPathOverrides,
  type PathColor,
  type PathPoint,
  type PredefinedPathId,
} from 'react-webgl-fluid-play';
  • Canvas default export (and named export)
  • PathFollower class export
  • predefined path exports: PREDEFINED_PATHS, PREDEFINED_PATH_IDS, createPredefinedPath, getPredefinedPath, getPredefinedPaths, isPredefinedPathId, cloneFluidPath
  • path/type exports: FluidPath, FluidPathOverrides, PathPoint, PathColor, PredefinedPathId
  • canvas API types: CanvasRef, CanvasProps, CanvasAnimationOptions, CanvasInitialAnimation

Predefined path ids

oval, infinity, spiral, wave, star, heart


Props

| Prop | Type | Default | Description | |---|---|---|---| | width | number | window width | Fixed pixel width of the canvas. Omit to auto-track the viewport. | | height | number | window height | Fixed pixel height of the canvas. Omit to auto-track the viewport. | | className | string | — | CSS class applied to the outer wrapper <div>. | | style | React.CSSProperties | — | Inline styles merged into the outer wrapper <div>. | | onLoad | () => void | — | Called once the WebGL fluid simulation has successfully initialised. | | onError | (error: string) => void | — | Called if WebGL initialisation fails, with a human-readable error message. | | showPathManager | boolean | false | Show the built-in path-selector button and panel. Omit or set false for a completely bare canvas. | | initialAnimation | { path: FluidPath \| PredefinedPathId; options?: CanvasAnimationOptions } | — | Starts a path animation automatically once the simulation is ready. | | loadingFallback | React.ReactNode | spinning ring | Rendered while the simulation is initialising. Pass null to show nothing. | | errorFallback | (error: string, retry: () => void) => React.ReactNode | minimal error + retry button | Rendered when initialisation fails. Receives the error message and a retry callback. |

CanvasAnimationOptions

  • maxLoops?: number (-1 or omitted = infinite)
  • predefinedOverrides?: FluidPathOverrides (used when path is a predefined id)

CanvasRef

  • playPath(path, options?)
  • playPredefinedPath(pathId, options?)
  • stopPath()
  • clearPath() (alias of stopPath())
  • isPlayingPath()
  • getCurrentPath()

Examples

Bare canvas — no UI whatsoever

<Canvas />

With the built-in path manager

<Canvas showPathManager />

Start a predefined path on init

<Canvas initialAnimation={{ path: 'heart', options: { maxLoops: 1 } }} />

Fixed size

<Canvas width={800} height={600} />

Lifecycle callbacks

<Canvas
  onLoad={() => console.log('simulation ready')}
  onError={(msg) => console.error('WebGL failed:', msg)}
/>

Custom loading indicator

<Canvas loadingFallback={<MySpinner />} />

// hide loading UI entirely
<Canvas loadingFallback={null} />

Custom error UI

<Canvas
  errorFallback={(error, retry) => (
    <div>
      <p>{error}</p>
      <button onClick={retry}>Try again</button>
    </div>
  )}
/>

Styled wrapper

<Canvas
  className="my-fluid-canvas"
  style={{ borderRadius: '12px', overflow: 'hidden' }}
/>

Use forwardRef to trigger animations imperatively

import { useRef } from 'react';
import Canvas, { type CanvasRef, createPredefinedPath } from 'react-webgl-fluid-play';

export default function App() {
  const canvasRef = useRef<CanvasRef>(null);

  return (
    <>
      <button onClick={() => canvasRef.current?.playPredefinedPath('infinity')}>Infinity</button>
      <button onClick={() => canvasRef.current?.playPredefinedPath('heart', { maxLoops: 1 })}>
        Heart Once
      </button>
      <button
        onClick={() => {
          const custom = createPredefinedPath('wave', { duration: 3, loop: false });
          canvasRef.current?.playPath(custom);
        }}
      >
        Custom Wave Variant
      </button>
      <button onClick={() => canvasRef.current?.stopPath()}>Stop</button>
      <Canvas ref={canvasRef} />
    </>
  );
}

Notes

  • Mobile: the component automatically uses window.visualViewport for accurate dimensions on mobile browsers and prevents default touch scroll/zoom behaviour on the canvas.
  • Dimensions: when width/height props are omitted the canvas tracks window.innerWidth / window.innerHeight (and visualViewport on mobile), reacting to resize and orientation-change events.
  • Path coordinates: custom paths use normalized coordinates (x/y in [0,1]).
  • WebGL: requires a browser with WebGL support and hardware acceleration enabled. The onError callback (or the default error UI) is shown when the context cannot be created.
  • Layout: the component renders as position: relative; width: 100%; height: 100%. Place it inside a container that defines the actual size (e.g. position: fixed; inset: 0 for full-screen).

Development scripts

  • pnpm start - run the demo app in development mode.
  • pnpm build - build the demo app (react-scripts build).
  • pnpm build:lib - build library artifacts to dist/ (CJS, ESM, and .d.ts).
  • pnpm lint - lint src/ and scripts/ with ESLint.
  • pnpm lint:fix - lint with auto-fixes.
  • pnpm prepublishOnly - build library artifacts before publishing.

Package metadata

  • Module formats: ships both CommonJS (main) and ESM (module) builds.
  • Type declarations: includes bundled TypeScript definitions (types).
  • Tree-shaking: no side effects, so bundlers can safely drop unused imports.

License

MIT © Maifee Ul Asad