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

@pieai/swimmer-render-kit

v0.5.0

Published

Shared Three.js colour pipeline contract, grade presets, and single-encode guard for PieAI Web3D products.

Readme

SwimmerRenderKit

Shared Three.js colour pipeline contract, grade presets, and a single-encode guard for PieAI Web3D products.

Implements SPEC-0007.

What this package is for

Six PieAI Web3D products need the same picture quality. Before this package, the same colour grade had been written three times — once tuned in YaZu, re-derived by hand in Break, and started again in weaker form in OwnMySpace.

Installing a package does not fix that: @react-three/postprocessing was already in three of those products, and absent from the one with the best picture. What is actually hard, and therefore worth sharing, is:

  1. the pipeline order — linear scene target → ACES → grade → exactly one sRGB encode — which fails silently when it is wrong;
  2. a set of named starting looks, so a project begins from something tuned;
  3. a guard against double tone-mapping and double sRGB encoding.

What it deliberately does not own

  • The EffectComposer, its passes, or their ordering.
  • Bloom, ambient occlusion, SMAA, or any other effect.
  • Any React or R3F component. three is an optional peer; there is no React dependency.
  • Per-project constants. Presets are starting points, not finished looks.
  • Lighting. A grade cannot rescue an unlit scene.

Install

pnpm add @pieai/swimmer-render-kit

Use

Optional colored-clay surface (0.4)

/clay exports a structural shader adapter, not a material, renderer, texture, lighting rig or post pass. It retains the host's Standard/Physical surface, color maps, skinning and instancing. Object-space kneading and filtered tool marks drive coupled pigment, roughness and shallow normals, without displacement.

import { createClaySurfaceAdapter } from '@pieai/swimmer-render-kit/clay';
const clay = createClaySurfaceAdapter({ amount: 1, scale: 1.5, relief: 0.12 });
const previous = material.onBeforeCompile;
const previousKey = material.customProgramCacheKey();
material.onBeforeCompile = (shader, renderer) => {
  previous.call(material, shader, renderer);
  clay.onBeforeCompile(shader);
};
material.customProgramCacheKey = () => `${previousKey}/${clay.customProgramCacheKey()}`;
clay.setAmount(0); // Uniform update: original response, no geometry rebuild.

Use an owned clone when source materials are shared by a loader or avatar kit. The host owns disposal, source restoration, role-specific scales and saved user preferences. For an opaque clay variant, disable Physical transmission and anisotropy on the clone; this shader does not change transparency or render queues. Coat, sheen and iridescence are attenuated by the appearance amount. No dependency on Three.js runtime or React is added. Missing shader anchors fail before either stage is modified; existing grade/output ownership is unchanged.

Opt-in polymer finish (0.5)

For a quieter hand-formed polymer-clay surface, use the same adapter with finish: 'polymer'. The 0.4 sculpted finish remains the default, unchanged.

const clay = createClaySurfaceAdapter({
  finish: 'polymer', scale: 1.5, relief: 0.018, grain: 0.65, roughness: 0.84,
});

Polymer has no directed sinusoidal furrows: scattered broad pressure and independently filtered fine grain use C2-continuous object-space fields. Grain slope is amplitude-normalized by frequency and adjusted for model scale; pigment variation remains shallow, without bleaching the host's colors. grain is in [0,1], roughness in [0.55,0.98]. Existing amount, scale, relief and pigment retain their contracts. Selecting a finish changes the program key; switching requires compiling from the original shader, not stacking finishes on an already patched program. These are material starting points, not a promise that arbitrary sharp geometry will look hand-sculpted.

Pick a preset and re-measure the pivot

import { defineGrade, srgbToDisplayLinear } from '@pieai/swimmer-render-kit';

// Measure your ungraded midtone (p50) in sRGB 8-bit from a controlled capture,
// then convert. Do NOT pass p50/255 — that is the common mistake.
export const grade = defineGrade('diorama', {
  contrastPivot: srgbToDisplayLinear(72.4),
});

The pivot is the one value you must set yourself. YaZu's donor used 0.5, correct for a dungeon lit so midtones sit near half, and wrong for YaZu, whose measured midtone is 0.066 display-linear — expanding around 0.5 pushed the whole frame down.

Raw EffectComposer path

The final blit owns ACES and the sRGB encode, so the renderer must own neither.

import { buildGradeFragment, GRADE_VERTEX_SHADER, createGradeUniformValues }
  from '@pieai/swimmer-render-kit/shader';

renderer.toneMapping = THREE.NoToneMapping;
renderer.outputColorSpace = THREE.LinearSRGBColorSpace;

const material = new THREE.RawShaderMaterial({
  name: 'PieSwimmerGradeOutput',
  defines: { ACES_FILMIC_TONE_MAPPING: '', SRGB_TRANSFER: '' },
  uniforms: toThreeUniforms(createGradeUniformValues(grade)),
  vertexShader: GRADE_VERTEX_SHADER,
  fragmentShader: buildGradeFragment(grade, { target: 'standalone' }),
});

RawShaderMaterial matters: a plain ShaderMaterial lets three inject a second tonemap and encode.

@react-three/postprocessing path

The library's composer already applies tone mapping and the encode, so the effect body must add neither.

import { buildGradeFragment } from '@pieai/swimmer-render-kit/shader';
import { Effect } from 'postprocessing';

class PieGrade extends Effect {
  constructor() {
    super('PieGrade', buildGradeFragment(grade, { target: 'main-image' }), {
      uniforms: new Map(/* ... */),
    });
  }
}

This target omits the tilt-shift blur, because a mainImage body has no texture handle to sample neighbours from. Use a separate depth-of-field effect if you need it.

Guard the pipeline

import { assertSingleColorEncode } from '@pieai/swimmer-render-kit/guard';

if (import.meta.env.DEV) {
  assertSingleColorEncode(renderer, gradeFragmentSources);
}

Throws when the frame tone-maps or sRGB-encodes anything other than exactly once. This package runs in the browser and does not sniff NODE_ENV; gate the call behind your own dev flag, or pass { throwOnFailure: false } and log the report.

Pass sources, not passes. three's Pass exposes neither name nor material, so composer.passes is opaque. Hand over an array of fragment source strings — the guard accepts those directly.

Say who owns the output when it is not obvious. There are three owners and they count differently:

| outputOwner | Who writes the final frame | What is counted | | --- | --- | --- | | renderer | three, straight to canvas | renderer.toneMapping and outputColorSpace | | composer | a blit you can read (standalone) | only the pass sources | | library | @react-three/postprocessing | an implicit 1 + 1, plus anything your effect adds |

The guard infers composer when a source encodes to sRGB, and renderer otherwise. It cannot infer library, because a correct main-image body looks identical to an empty one — declare it on that path:

assertSingleColorEncode(renderer, [effectFragment], { outputOwner: 'library' });

The distinction is not pedantry. On the composer path the renderer's settings are inert — three skips tone mapping and the encode on the intermediate targets a composer renders into. A product whose grade can be switched off must leave the renderer capable of encoding for the ungraded frames, and that is correct configuration, not a mistake to fix so an assertion goes green. YaZu ships exactly that.

Presets

| Preset | For | | --- | --- | | neutral | Correct pipeline, no look. Satisfies the colour-pipeline baseline before art direction lands. | | diorama | Miniature/tilt-shift board look. Values measured on YaZu's mission board. | | night-street | Night exterior; cool shadows pushed, highlights held back so practicals stay hot. | | stage | Single lit subject on a controlled background; minimal vignette. |

Verify

pnpm install
pnpm verify