Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 2 additions & 2 deletions .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
"$schema": "https://unpkg.com/@changesets/config@3.1.4/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [["@lovo/matter", "@lovo/matter-react", "@lovo/matter-cli"]],
"fixed": [["@mattermix/shaders", "@mattermix/shaders-react", "@mattermix/shaders-cli"]],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": ["@matter/registry", "@matter/tsconfig", "@matter/docs", "@matter/docs-tests"],
"ignore": ["@shaders/registry", "@shaders/tsconfig", "@shaders/docs", "@shaders/docs-tests"],
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
"onlyUpdatePeerDependentsWhenOutOfRange": true
}
Expand Down
2 changes: 1 addition & 1 deletion .changeset/stable-hash-backend-parity.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
'@lovo/matter': minor
'@mattermix/shaders': minor
---

Seeded randomness now renders the same pattern on the WebGPU and WebGL2 backends. three's TSL `hash()` writes its PCG constants as float literals, which GLSL rounds to a different hash than WGSL computes, so the same `seed` produced a different Voronoi layout in Safari than in Chrome. The new `stableHash` and `stableHashUint` exports run the same PCG with integer-typed constants and chain hash streams u32-to-u32, and `voronoiCells`, `grain`, `metaballs`, and `ditherPattern` now draw from them.
Expand Down
4 changes: 2 additions & 2 deletions .claude/skills/resolve-coderabbit-feedback/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ pnpm test --filter <touched package>

Four repo traps apply here:

- If a fix changed source under `packages/matter` or `packages/matter-react`, run `pnpm --filter @lovo/matter build`. The docs site consumes `dist`, so an unbuilt fix looks like no fix at all.
- If a fix changed source under `packages/shaders` or `packages/shaders-react`, run `pnpm --filter @mattermix/shaders build`. The docs site consumes `dist`, so an unbuilt fix looks like no fix at all.
- If a fix changed a dependency in any `package.json`, commit the updated `pnpm-lock.yaml` with it, and check that the lockfile still pins `node@runtime` at `version: 22.22.2` with `hasBin: true`. Every pnpm resolution step rewrites that entry to `0.0.0`, and CI then dies at install in every job.
- Never run `pnpm snap` as part of this workflow. Ask first. It needs Docker and Node 22, it takes a long time, and it corrupts a running docs or editor dev server.
- If you ran Playwright or `pnpm snap` for any reason, tell the user to restart the dev server before trusting the browser. The procedure is in `AGENTS.md` under the environment gotchas.
Expand Down Expand Up @@ -296,7 +296,7 @@ Pick `<type>` from the file class the approved fixes touched, and add no AI attr
| A workflow under `.github/` | `ci` |
| Tests, tooling config, or a lockfile on its own | `chore` |

The command above supplies the colon, so these values carry none. Scope is the package name without the `@lovo/` prefix. When a run spans classes, name the class that carries the substantive fix, so a code fix that drags a lockfile with it stays `fix(<scope>)`. The user already saw the commit line in the Step 6 preview, so change it there rather than asking again here.
The command above supplies the colon, so these values carry none. Scope is the package name without the `@mattermix/` prefix. When a run spans classes, name the class that carries the substantive fix, so a code fix that drags a lockfile with it stays `fix(<scope>)`. The user already saw the commit line in the Step 6 preview, so change it there rather than asking again here.

## Step 10: Reply and resolve the threads

Expand Down
2 changes: 1 addition & 1 deletion .fallowrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"apps/docs/content/**",
"**/*.mdx",
"apps/playground/**",
"packages/matter-cli/src/test-fixtures/**"
"packages/shaders-cli/src/test-fixtures/**"
],
"ignoreDependencies": [
"@tweakpane/core",
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ jobs:
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm build
- run: pnpm --filter @matter/docs-tests test:visual
- run: pnpm --filter @shaders/docs-tests test:visual
- name: Upload visual diffs on failure
if: failure()
uses: actions/upload-artifact@v4
Expand Down
2 changes: 1 addition & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ pnpm-lock.yaml
# Isolated workspaces / vendored fixtures
.worktrees/
.fallow/
packages/matter-cli/src/test-fixtures/
packages/shaders-cli/src/test-fixtures/
28 changes: 14 additions & 14 deletions AGENTS.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2026 Hunter Garrett / Lovo
Copyright (c) 2026 Hunter Garrett / Mattermix

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
28 changes: 14 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
# Matter
# Shaders

React shader components powered by WebGPU and Three.js TSL.

> **Status:** v0.1.0 shipped to npm. `npm install -D @lovo/matter-cli && npx matter-cli init && npx matter-cli add linear-gradient` to scaffold your first component.
> **Status:** v0.1.0 shipped to npm. `npm install -D @mattermix/shaders-cli && npx shaders-cli init && npx shaders-cli add linear-gradient` to scaffold your first component.

## What is Matter?
## What is Shaders?

Matter is a React component library for shader-driven backgrounds and interactive surfaces. It ships polished drop-in components like `<LinearGradient>`, `<Aurora>`, and `<DotField>` for developers who don't want to write shaders, alongside a primitives library and recipe gallery for those who do.
Shaders is a React component library for shader-driven backgrounds and interactive surfaces. It ships polished drop-in components like `<LinearGradient>`, `<Aurora>`, and `<DotField>` for developers who don't want to write shaders, alongside a primitives library and recipe gallery for those who do.

## Repository structure

```
apps/
├── docs/ # @matter/docs — Next.js docs site (Tweakpane-driven demos)
└── playground/ # @matter/playground — Vite app with M1 manual harnesses
├── docs/ # @shaders/docs — Next.js docs site (Tweakpane-driven demos)
└── playground/ # @shaders/playground — Vite app with M1 manual harnesses

packages/
├── matter/ # @lovo/matter — engine: TSL primitives, renderer, scheduler
├── matter-react/ # @lovo/matter-react — React binding
└── matter-cli/ # @lovo/matter-cli — copy-paste CLI
├── shaders/ # @mattermix/shaders — engine: TSL primitives, renderer, scheduler
├── shaders-react/ # @mattermix/shaders-react — React binding
└── shaders-cli/ # @mattermix/shaders-cli — copy-paste CLI

registry/ # @matter/registry — Tier 1 component source files (CLI consumes)
registry/ # @shaders/registry — Tier 1 component source files (CLI consumes)

tooling/
├── eslint-config/ # shared ESLint flat config
Expand All @@ -41,20 +41,20 @@ pnpm install
pnpm build # build all packages + apps
pnpm typecheck # typecheck all packages + apps
pnpm lint # lint all packages + apps
pnpm test # run all tests (Vitest in @lovo/matter)
pnpm test # run all tests (Vitest in @mattermix/shaders)

# Live shader demo
pnpm --filter @matter/docs dev # Next.js docs at http://localhost:3000
pnpm --filter @shaders/docs dev # Next.js docs at http://localhost:3000

# Engine playground (per-phase manual harnesses)
pnpm --filter @matter/playground dev # Vite at http://localhost:5173
pnpm --filter @shaders/playground dev # Vite at http://localhost:5173
```

## Roadmap

- ✅ **Milestone 0** — Repo bootstrap
- ✅ **Milestone 1** — Vertical slice: `<LinearGradient>` end-to-end
- ✅ **Milestone 2** — `@lovo/matter-cli` (copy-paste delivery)
- ✅ **Milestone 2** — `@mattermix/shaders-cli` (copy-paste delivery)
- ✅ **Milestone 3** — The other 5 v1 components (MeshGradient, Aurora, DotField, NoiseField, Waves)
- ✅ **Milestone 4** — Docs site polish
- ✅ **Milestone 5** — Performance, testing, accessibility
Expand Down
2 changes: 1 addition & 1 deletion apps/docs-tests/editor/export-import.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ test('export, reload, import round-trips the graph', async ({ page }) => {

const download = await downloadPromise;

expect(download.suggestedFilename()).toBe('matter-graph.json');
expect(download.suggestedFilename()).toBe('shaders-graph.json');

const downloadPath = await download.path();

Expand Down
4 changes: 2 additions & 2 deletions apps/docs-tests/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@matter/docs-tests",
"name": "@shaders/docs-tests",
"version": "0.0.0",
"private": true,
"type": "module",
Expand All @@ -12,7 +12,7 @@
},
"devDependencies": {
"@axe-core/playwright": "^4.10.0",
"@matter/tsconfig": "workspace:*",
"@shaders/tsconfig": "workspace:*",
"@playwright/test": "^1.48.0",
"@types/node": "^22",
"typescript": "^5"
Expand Down
4 changes: 2 additions & 2 deletions apps/docs-tests/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default defineConfig({
// than running the dev server, so without the flag those specs 404. The
// deploy build omits it, which is the point — see next.config.ts.
command:
'INCLUDE_DEV_ROUTES=1 pnpm turbo run build --filter=@matter/docs --force && pnpm --filter @matter/docs preview',
'INCLUDE_DEV_ROUTES=1 pnpm turbo run build --filter=@shaders/docs --force && pnpm --filter @shaders/docs preview',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
timeout: 180_000,
Expand All @@ -34,7 +34,7 @@ export default defineConfig({
// build as the docs entry above, and the same dev-routes flag, which is
// what puts the parity routes in the bundle these specs load.
command:
'INCLUDE_DEV_ROUTES=1 pnpm turbo run build --filter=@matter/editor --force && pnpm --filter @matter/editor preview',
'INCLUDE_DEV_ROUTES=1 pnpm turbo run build --filter=@shaders/editor --force && pnpm --filter @shaders/editor preview',
url: 'http://localhost:3010',
reuseExistingServer: !process.env.CI,
timeout: 180_000,
Expand Down
2 changes: 1 addition & 1 deletion apps/docs-tests/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"extends": "@matter/tsconfig/library.json",
"extends": "@shaders/tsconfig/library.json",
"compilerOptions": {
"noEmit": true,
"module": "ESNext",
Expand Down
2 changes: 1 addition & 1 deletion apps/docs-tests/visual/gamut.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { waitForShader } from './helpers';
* the P3-vs-sRGB difference we'd want to measure. So output-widening is validated
* by eye on a P3 display; the deterministic automated proof that wide-gamut colors
* decode to extended linear-sRGB lives in the `parseColorString` unit tests
* (`packages/matter`). This spec guards that the P3 output path itself stays alive
* (`packages/shaders`). This spec guards that the P3 output path itself stays alive
* (the manual context.configure into Display P3 doesn't throw or blank the canvas).
*/
test('gamut probe — sRGB and P3 output paths both render without error', async ({ page }) => {
Expand Down
4 changes: 2 additions & 2 deletions apps/docs-tests/visual/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import type { Page } from '@playwright/test';

/**
* Waits for the shader to finish its first deterministic frame and settle.
* Pages set `window.__matterTestReady = true` via VisualTestPause after the
* Pages set `window.__shadersTestReady = true` via VisualTestPause after the
* second frame. The extra 50ms absorbs any micro-jitter before the screenshot.
* The timeout budgets for SwiftShader on 2-core CI runners, where a heavy
* raymarch (aurora: 60 slices x 5 fbm octaves) needs several seconds per frame.
*/
export async function waitForShader(page: Page): Promise<void> {
await page.waitForFunction(
() => (window as unknown as { __matterTestReady?: boolean }).__matterTestReady === true,
() => (window as unknown as { __shadersTestReady?: boolean }).__shadersTestReady === true,
undefined,
{ timeout: 60_000 },
);
Expand Down
30 changes: 15 additions & 15 deletions apps/docs/content/docs/changelog.mdx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
---
title: Changelog
description: Notable changes per release across the Matter packages.
description: Notable changes per release across the Shaders packages.
section: overview
order: 30
---

# Changelog

Matter ships as three coordinated npm packages: `@lovo/matter` (engine), `@lovo/matter-react` (React binding), and `@lovo/matter-cli` (copy-paste CLI). Each package has its own `CHANGELOG.md` in the repo; this page summarizes the headline changes.
Shaders ships as three coordinated npm packages: `@mattermix/shaders` (engine), `@mattermix/shaders-react` (React binding), and `@mattermix/shaders-cli` (copy-paste CLI). Each package has its own `CHANGELOG.md` in the repo; this page summarizes the headline changes.

## Unreleased

**Breaking change — `Waves` is now `WaveLines`.**

The old name read as water. The component draws glowing lines, so it's renamed to match: add it with `matter-cli add wave-lines`, import `WaveLines`, and pass `lines` instead of `layers` (the per-line type is `WaveLine`, formerly `WaveLayer`). Nothing else moved: same props, same rendering, no visual change.
The old name read as water. The component draws glowing lines, so it's renamed to match: add it with `shaders-cli add wave-lines`, import `WaveLines`, and pass `lines` instead of `layers` (the per-line type is `WaveLine`, formerly `WaveLayer`). Nothing else moved: same props, same rendering, no visual change.

Components ship by copy-paste, so installed copies keep working under the old name; re-add `wave-lines` through the CLI to pick up the new one.

Expand Down Expand Up @@ -43,32 +43,32 @@ Same copy-paste rule: installed copies keep rendering as before; re-add `waves`

## 0.2.0

**Breaking change — `@lovo/matter` no longer re-exports pure TSL primitives.**
**Breaking change — `@mattermix/shaders` no longer re-exports pure TSL primitives.**

The following 15 nodes are no longer exported by `@lovo/matter`. Import them directly from `three/tsl`:
The following 15 nodes are no longer exported by `@mattermix/shaders`. Import them directly from `three/tsl`:

`uv`, `vec2`, `vec3`, `vec4`, `uniform`, `mix`, `smoothstep`, `mod`, `sin`, `cos`, `length`, `dot`, `normalize`, `max`, `min`

```ts
// Before (0.1.x)
import { vec3, uv, time } from '@lovo/matter'
import { vec3, uv, time } from '@mattermix/shaders'

// After (0.2.0)
import { vec3, uv } from 'three/tsl'
import { time } from '@lovo/matter' // still here — reduced-motion-gated
import { time } from '@mattermix/shaders' // still here — reduced-motion-gated
```

`time` continues to be exported from `@lovo/matter` because Matter owns its semantics (reduced-motion gating). For raw uncapped time, import from `three/tsl` directly.
`time` continues to be exported from `@mattermix/shaders` because Matter owns its semantics (reduced-motion gating). For raw uncapped time, import from `three/tsl` directly.

All Matter-owned primitives (`fbm`, `noise`, `voronoi`, `colorRamp`, `sdfCircle`, `displace`, `cursorRipple`, `quantize`) remain exported from `@lovo/matter` unchanged. Registry component sources at 0.2.0 use the new convention. If you copied a component at 0.1.x, update its imports from `@lovo/matter` to `three/tsl` for the dropped symbols (or re-add the component via the CLI to pull the 0.2.0 source).
All Matter-owned primitives (`fbm`, `noise`, `voronoi`, `colorRamp`, `sdfCircle`, `displace`, `cursorRipple`, `quantize`) remain exported from `@mattermix/shaders` unchanged. Registry component sources at 0.2.0 use the new convention. If you copied a component at 0.1.x, update its imports from `@mattermix/shaders` to `three/tsl` for the dropped symbols (or re-add the component via the CLI to pull the 0.2.0 source).

**Why:** Re-exporting pure TSL primitives provided no value beyond shared import paths — no rename, no added docs, no future-swap benefit. Dropping them clarifies the layer boundary: Matter ships value-add primitives, TSL provides the math. See the [matter engine reference](/reference/matter) for the current public surface.
**Why:** Re-exporting pure TSL primitives provided no value beyond shared import paths — no rename, no added docs, no future-swap benefit. Dropping them clarifies the layer boundary: Matter ships value-add primitives, TSL provides the math. See the [shaders engine reference](/reference/shaders) for the current public surface.

## 0.1.0 — Initial public release

The first public release of Matter — React shader components on WebGPU + Three.js TSL.

### `@lovo/matter`
### `@mattermix/shaders`

Framework-agnostic engine. Exports:

Expand All @@ -77,7 +77,7 @@ Framework-agnostic engine. Exports:
- **Runtime:** `createRenderer`, `MatterScheduler`, visibility/intersection watchers, reduced-motion policy.
- **Inputs:** `CursorInput` (framework-agnostic cursor source).

### `@lovo/matter-react`
### `@mattermix/shaders-react`

React binding for the engine. Exports:

Expand All @@ -87,13 +87,13 @@ React binding for the engine. Exports:
- Animation glue — `useAnimatableUniform`, accepts any MotionValue-shaped signal.
- Fallback / dev utilities — `FallbackBoundary`, `useStaticHint`, `MatterMonitor`.

### `@lovo/matter-cli`
### `@mattermix/shaders-cli`

shadcn-style copy-paste CLI: `init`, `list`, `add`, `update`. The default registry tracks the CLI's published version tag (`v0.1.0`), so the component source you copy is stable per release.

### v1 components

Available via `matter-cli add <name>`: `linear-gradient`, `mesh-gradient`, `aurora`, `dot-field`, `simplex-noise`, `waves`. Each lands in your project as a regular `.tsx` file — you own and edit it from that point.
Available via `shaders-cli add <name>`: `linear-gradient`, `mesh-gradient`, `aurora`, `dot-field`, `simplex-noise`, `waves`. Each lands in your project as a regular `.tsx` file — you own and edit it from that point.

### Requirements

Expand All @@ -102,5 +102,5 @@ Available via `matter-cli add <name>`: `linear-gradient`, `mesh-gradient`, `auro
- React ^19, Three.js ^0.170.

<Callout>
Detailed per-package changelogs live in the repo: [`packages/matter/CHANGELOG.md`](https://github.com/lovo-hq/matter/blob/main/packages/matter/CHANGELOG.md), [`packages/matter-react/CHANGELOG.md`](https://github.com/lovo-hq/matter/blob/main/packages/matter-react/CHANGELOG.md), [`packages/matter-cli/CHANGELOG.md`](https://github.com/lovo-hq/matter/blob/main/packages/matter-cli/CHANGELOG.md).
Detailed per-package changelogs live in the repo: [`packages/shaders/CHANGELOG.md`](https://github.com/mattermix/shaders/blob/main/packages/shaders/CHANGELOG.md), [`packages/shaders-react/CHANGELOG.md`](https://github.com/mattermix/shaders/blob/main/packages/shaders-react/CHANGELOG.md), [`packages/shaders-cli/CHANGELOG.md`](https://github.com/mattermix/shaders/blob/main/packages/shaders-cli/CHANGELOG.md).
</Callout>
Loading
Loading