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

jelly-progress-bar

v0.1.0

Published

GPU-accelerated "liquid fill" progress bar for React. A fullscreen-quad Three.js ShaderMaterial does all per-pixel work; the CPU only integrates two springs.

Readme

jelly-progress-bar

GPU-accelerated "liquid fill" progress bar for React. A single fullscreen-quad WebGL shader does all per-pixel work (wavy jelly edge, big-wave light field, waterline glow, squeeze pulse); the CPU only integrates two tiny spring scalars. Colors and every visual parameter are props — no CSS framework required.

MIT npm version ESM only

Extracted from the training pipeline of splatscene, a Tauri 3D Gaussian Splatting app. The shipped defaults are the exact visuals that app settled on.


Why?

| | Typical progress bar | JellyProgressBar | | --- | --- | --- | | Fill | static color / CSS transition | wavy jelly waterline that bounces on every value jump | | Body | flat color block | big-wave light field, crests anchored in space | | Edge | none | diffusing waterline glow (asymmetric, both sides) | | Motion | none | squeeze pulse band near the moving edge | | CPU cost | — | 2 scalar springs per frame (2–4 float ops) | | Rendering | DOM/CSS | one WebGL quad, every pixel computed in parallel on the GPU |

This is the component to reach for when you have a GPU already in the page (three.js, gsplat viewers, dashboards) and want a progress bar that reads as alive — a liquid filling a vessel — for free on the CPU.

Install

npm i jelly-progress-bar

Peer dependencies (not bundled): react ≥18 <20 and three ≥0.150. Consumers already bundling three pay nothing extra.

Quick start

import { JellyProgressBar } from "jelly-progress-bar";

export function TrainingPipeline() {
  return (
    <JellyProgressBar value={42} height={64}>
      <span>Training… 42%</span>
    </JellyProgressBar>
  );
}

value is a target level 0–100. The waterline eases toward it every frame and, when the target jumps, bounces like jelly before settling. children render as an overlay on top of the bar (stage label + live percent + metrics).

Props

| Prop | Type | Default | Description | | --- | --- | --- | --- | | value | number | — (required) | Target level 0–100. Read live every frame; the eased display level follows with a first-order lag. | | children | ReactNode | — | Overlay content rendered above the WebGL canvas (e.g. stage label + percent). | | height | number | 64 | Bar height in px. | | className | string | — | Extra class names forwarded to the root element (jpb-root is always applied). | | tuning | Partial<Tuning> | — | Visual tuning overrides, merged over DEFAULT_TUNING. See Tuning. | | colors | Partial<Colors> | — | Color overrides, merged over DEFAULT_COLORS. See Colors. | | cornerRadius | number | tuning.cornerRadius | Convenience override for the corner radius in px (shell borderRadius + shader rounded-box mask stay in sync). |

All props update live — no renderer rebuild, no remount. Change tuning, colors, cornerRadius, height, or value mid-flight and the next frame reflects it.

Tuning

15 visual parameters. Pass a partial object; unspecified keys keep their defaults.

| Key | Default | Range (sane) | What it does | | --- | --- | --- | --- | | edgeGlowThick | 0.3 | 0.05–1 | Half-width of the waterline glow band, in px-scale multiples. | | edgeGlowBright | 1.15 | 0–2 | Multiplier on the waterline glow color. | | edgeGlowLeft | 9 | 1–20 | How far the glow diffuses behind the edge (into the filled side). | | edgeGlowRight | 1 | 1–20 | How far the glow diffuses ahead of the edge (into the unfilled side). | | edgeGlowSpill | 6 | 0–20 | Extra px of filled color that "spills" past the wavy edge before fading. | | jellyBounce | 0.3 | 0–1 | Underdamping of the jelly spring — 0 is sluggish, 1 is springy/bouncy. | | waveAmp | 0.75 | 0–2 | Amplitude of the big-wave light/dark field inside the fill. | | waveScale | 0.5 | 0.2–2 | Wavelength scale of the big waves (larger = longer, smoother waves). | | waveSpeed | 1 | 0–3 | How fast the wave phase drifts. | | morphAmp | 0.25 | 0–1 | How much the wave crests warp/morph over time. | | squeezeAmt | 0.045 | 0–0.2 | Strength of the squeeze pulse: while the edge moves, a band near it brightens. | | glowExp | 2.5 | 0.5–5 | Fall-off exponent dimming the waves toward the left wall (1 = linear). | | baseBright | 0.85 | 0–2 | Brightness of the base fill gradient. | | cornerRadius | 0 | 0– | Corner radius of the box (px). 0 = square. Also sets the shell borderRadius. | | levelDamp | 0.7 | 0.1–1 | Damping of the first-order level lag — how quickly the target eases into position. |

<JellyProgressBar
  value={60}
  tuning={{ jellyBounce: 0.6, waveAmp: 1.1, cornerRadius: 8 }}
/>

Colors

Four colors. Each is a hex string (#RGB or #RRGGBB). Invalid values throw immediately (Invalid hex color "…").

| Key | Default | Description | | --- | --- | --- | | baseTop | #7F3C14 | Fill gradient color at the top of the bar. | | baseBottom | #4B1407 | Fill gradient color at the bottom of the bar. | | waterlineGlow | #FFE8A2 | Color of the glow hugging the moving edge. | | track | #12090c | Background color of the empty bar (also the shell background). |

<JellyProgressBar
  value={80}
  colors={{ baseTop: "#1E88E5", baseBottom: "#0D47A1", waterlineGlow: "#80DEEA", track: "#0A1929" }}
/>

The fill gradient is mix(baseBottom, baseTop, 1 − y/uH) — top color at the top, bottom color at the bottom, smoothly interpolated by the fragment shader.

Exports

import {
  JellyProgressBar,          // component
  DEFAULT_TUNING,            // { …15 defaults }
  mergeTuning,               // Partial<Tuning> → Tuning (sanitizes NaN/Infinity)
  type Tuning,
  DEFAULT_COLORS,            // { baseTop, baseBottom, waterlineGlow, track }
  mergeColors,               // Partial<Colors> → Colors (validates hex)
  normalizeHex, hexToRgb,    // color utils (hexToRgb → normalized 0–1 [r,g,b])
  type Colors,
  cx, type ClassInput,       // tiny class-join helper
  clamp, easeLevel,
  stepJelly, stepSqueeze,    // the CPU spring physics, pure & testable
  type SpringState,
} from "jelly-progress-bar";

Accessibility

  • Renders role="progressbar" with aria-valuemin="0", aria-valuemax="100".
  • aria-valuenow is updated every tick from the eased level (via setAttribute, outside React's render cycle, so re-renders never clobber it).
  • Honors prefers-reduced-motion: the wave time freezes, the edge snaps to the target instantly, and the squeeze pulse is disabled.
  • The WebGL canvas is created inside useEffect in a try/catch. In environments without WebGL (jsdom, headless, SSR hydration) the component falls back to a styled track + overlay, and the canvas is simply absent.

Browser support

WebGL1+ (everything modern). The component clamps devicePixelRatio to 2 to bound fill-rate cost. ESM-only; pair it with any bundler. SSR-safe (all DOM work happens in effects).

Development

npm install
npm run demo:dev      # tune-slider playground (Vite, imports ../src live)
npm run typecheck     # tsc across src/test/demo
npm test              # vitest (pure physics/color tests + jsdom render test)
npm run build         # tsc → dist (ESM, .d.ts, source maps)
npm pack --dry-run    # inspect the tarball (dist + README + LICENSE only)

Architecture

For the deep dive — the 5-point visual model, the exact spring equations, the full uniform table with per-parameter semantics, the live-update flow, reduced motion / fallback behavior, performance notes, and a porting guide for vanilla / Canvas2D / WebGPU — see docs/ARCHITECTURE.md.

License

MIT © 2026 chrislin95