Back to directory
milesroxas

shader studio

by milesroxas
v3
^3.82.0
MIT

From the author

Author, publish and place WebGL shader looks from the Payload CMS admin. Looks are used like media: a visual slot references one, the site renders what is published.

Installation

npm i shader-studio

0

Stars

0

Forks

0

Open Issues

Today

Last Updated

0

Weekly Downloads

v

npm Version

Dependencies

GitHub stars

Trends appear after 7 days of data. Daily snapshots have not started yet.

npm weekly downloads

Trends appear after 7 days of data. Daily snapshots have not started yet.

Each point is npm's rolling 7-day download count.

Health Score

35/100Fair

Composite score based on GitHub activity, stars, npm downloads, publish recency, dependency count, and package size.

Badge for your README

Payload Directory health badge
Markdown
[![Payload Directory](https://payloaddirectory.dev/badge/1431540849-root.svg)](https://payloaddirectory.dev/plugins/1431540849-root)
HTML
<a href="https://payloaddirectory.dev/plugins/1431540849-root"><img src="https://payloaddirectory.dev/badge/1431540849-root.svg" alt="Payload Directory"></a>
Updated Oct 23, 2026Created Oct 23, 2026

README

shader-studio

npm CI License: MIT

A Payload CMS plugin for authoring, publishing and placing WebGL shader looks from the admin. Editors tune an effect on a live stage and publish it. Pages reference the published look like a media file and render it from a snapshot and a poster pair, so the site never waits on the GPU.

Features

  • Studio in the admin. A looks collection with a live stage, an Inspector generated from the effect's contract, stills, history and usage.
  • Publish renders. Dark and light posters (1600 × 900 WebP with alpha) and stills render in the editor's browser and publish with the look in one request.
  • Slots hydrate. studioLookField() stores a look id in any collection or global. On read the id becomes the published snapshot, posters, ground and stills.
  • Bring your own effects. An effect is a pure contract plus two React Three Fiber scenes. One example effect, wash, ships with the package.
  • Any database adapter. Postgres additionally takes a row lock on publish.

Requirements

Peer dependencyVersion
payload, @payloadcms/ui^3.82
react, react-dom^19
three>=0.160
@react-three/fiber^9
sharp>=0.32, passed to buildConfig({ sharp })

Installation

pnpm add shader-studio three @react-three/fiber

Quick start

Register the plugin with at least one effect and add a slot to a collection.

// payload.config.ts
import { buildConfig } from 'payload'
import { shaderStudioPlugin, studioLookField } from 'shader-studio'
import { washEffect } from 'shader-studio/effects/wash'
import sharp from 'sharp'

export default buildConfig({
  sharp,
  collections: [
    Users,
    Media,
    {
      slug: 'pages',
      versions: { drafts: true },
      fields: [
        { name: 'title', type: 'text', required: true },
        {
          name: 'hero',
          type: 'group',
          fields: [studioLookField({ effect: 'wash', label: 'Hero', placement: 'hero' })],
        },
      ],
    },
  ],
  plugins: [
    shaderStudioPlugin({
      effects: [{ contract: washEffect, client: 'shader-studio/effects/wash/client#WashEffect' }],
      onPublish: async ({ operation, id }) => {
        // revalidate pages that use looks
      },
    }),
  ],
})

Regenerate the import map so the admin can load the studio and your effect:

pnpm payload generate:importmap

In the admin, Studio Looks is a new collection. Create a look, tune it on the Studio tab and press Publish. On a page, the hero's Look field is a picker that creates a look for the slot and opens the studio in a drawer.

On the front end, read the hydrated slot with parseRelease:

import { parseRelease } from 'shader-studio'
import { washEffect } from 'shader-studio/effects/wash'

const release = parseRelease(washEffect, page.hero.look)
// release.snapshot.dark | .light   resolved tuning per face, for the live scene
// release.posters.dark.url | .light 1600 × 900 WebP, for reduced motion, no WebGL, cards
// release.ground                    the look's own background per face, if set
// release.stills?.preview           1200 × 630 share image, if the look has one

parseRelease returns null for an unpublished look, a look of another effect or a malformed snapshot. It never throws.

Configuration

Plugin options

OptionDefaultDescription
effectsrequired{ contract, client } per effect, in the order editors see them. The first is default.
enabledtruefalse keeps the collection schema but removes hooks, endpoints and admin components.
looksSlug'studio-looks'Slug of the looks collection. Do not rename once it has data.
labelsStudio Look / Studio LooksCollection labels.
admin.groupnoneAdmin navigation group.
slotField'look'Field name studioLookField() uses. Hydration and usage find slots by it.
mediaSlug'media'Upload collection renders are filed in.
mediaFolder'Shader Studio'Folder for renders when the media collection uses folders. false for none.
mediaData({ title }) => ({ alt: title })Extra data for every media document a render creates.
serializeMediafilename, url, width, height, mimeType, updatedAtFields a slot receives for each rendered file. Add what your image CDN needs.
ground#09090b / #f5f5f4Colour under an effect on each face: the stage background and the fill of opaque stills.
stillUsespreview, social, square, storyStatic renders a look offers. custom is always available.
accessany signed-in userWho may read, write and publish looks.
hydratetrueReplace slot ids with the published look on read.
foldersfalsePlace looks in Payload folders.
onPublishnoneCalled after publish and unpublish. Revalidate caches here.
overridesnoneModify the looks collection before it is added.

studioLookField()

ArgumentDefaultDescription
effectrequiredAn effect id, or { sibling: 'fieldName' } to read the id from a sibling field.
name'look'Must equal the plugin's slotField.
looksSlug'studio-looks'Must equal the plugin's looksSlug.
label'Slot'Used in a new look's title: "Page title · Hero".
placementfirst placementPlacement the studio opens at from this slot.
presetFieldnoneSibling text field holding a shipped look id. The picker offers shipped looks and writes it.
overridesnoneMerged over the relationship field.

When the host document is published, the field validates that the look is published and belongs to the slot's effect.

Documentation

  • Concepts: recipes, snapshots, posters, ground, stills, placements, history.
  • Writing an effect: the contract, copy, scenes and registration.
  • API: authoring looks over REST and the endpoints the plugin adds.

Development

pnpm install
cp dev/.env.example dev/.env
pnpm dev          # dev app at http://localhost:3002/admin ([email protected] / test)
pnpm test         # unit tests, then integration tests on in-memory SQLite
pnpm build        # dist/: types, JS and the compiled studio.css

See CONTRIBUTING.md for the project layout and release process.

License

MIT

Similar plugins