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

@classytic/vixel-schema

v0.13.0

Published

The VixelSpec composition contract: zero-dependency, agent-emittable TypeScript types shared by vixel (engine), vixel-ui (editor), and agents. No ffmpeg, no React.

Downloads

867

Readme

@classytic/vixel-schema

Sponsor

The VixelSpec composition contract — zero dependencies.

The single source of truth for the JSON that an agent emits, an editor edits, and every renderer renders. No ffmpeg, no React — types, pure edit primitives, and an opt-in validator.

@classytic/vixel-schema      ← the contract (this package)
   ├── @classytic/vixel            engine    → renders a spec to MP4 (server, ffmpeg)
   ├── @classytic/vixel-ui         editor    → previews + edits a spec (browser, Pixi)
   ├── @classytic/vixel-render-pixi export   → WYSIWYG server render (headless Pixi)
   ├── @classytic/vixel-agent      agent     → AI-SDK / MCP tools over the spec
   └── @classytic/vixel-studio     catalog   → templates / decks as pure spec data

Because everyone depends on this, the contract never drifts — and a frontend that mounts the editor never pulls ffmpeg into its dependency tree.

The spec in 30 seconds

Visual lanes hold any media (video / image / text / shape / effect), absolutely timed (at + duration), stacked in array order. A sequential lane is the "main track" (clips butt end-to-end). Audio lives on audio lanes. Transitions are first-class on the lane, between adjacent clips.

import { defineComposition } from '@classytic/vixel-schema';

export const spec = defineComposition({
  version: 1,
  output: { width: 1080, height: 1920, fps: 30 },
  tracks: [
    {
      type: 'visual',
      sequential: true, // main track — `at` is re-flowed for you on edits
      clips: [
        { media: { kind: 'video', source: 'a.mp4' }, at: 0, duration: 3 },
        { media: { kind: 'video', source: 'b.mp4', trimStart: 2 }, at: 3, duration: 4 },
      ],
      transitions: [{ between: [0, 1], transition: { id: 'fade', duration: 0.5 } }],
    },
    {
      type: 'visual', // overlay lane — composites on top
      clips: [
        {
          media: { kind: 'text', text: 'Hello' },
          at: 0.5,
          duration: 2,
          place: { region: 'lower-third' }, // semantic placement → resolved to transform.frame
        },
      ],
    },
    { type: 'audio', items: [{ source: 'music.mp3', at: 0, gain: -12 }] }, // gain in dB
  ],
});

Units worth knowing: transform.frame is a normalized rect (0..1 of the canvas), audio gain is dB (0 = unity), a clip's volume is linear 0..1.

Validate at every trust boundary

Core stays zero-dep; the validator is an opt-in subpath (zod loads only if you import it). Point it at anything untrusted — an agent's emission, an API ingest, a pasted project:

import { safeParseSpec, validateSpec } from '@classytic/vixel-schema/validate';

const r = safeParseSpec(json); // { success, data? , error? } — typed VixelSpec on success
const { valid, errors } = validateSpec(json); // errors as "path: message" lines —
// feed them straight back to the agent for a retry loop.

Two layers: structural (shape/enums/ranges) and semantic (every effect/transition id must resolve in the live registry, params range-checked against descriptors — so a registered BYO pack validates for free). Unknown keys are preserved, not stripped: a spec authored against a newer schema round-trips losslessly.

Normalize once, render anywhere

import { normalizeSpec } from '@classytic/vixel-schema';
const ready = normalizeSpec(spec);

Resolves agent-friendly shorthands into the explicit shape renderers read: semantic placetransform.frame, shape style presets inlined, stable ids minted on every track/clip/item/marker, transition between canonicalized to ids. Pure and idempotent.

The edit core — one reducer for UI, agents, and servers

Every edit is a typed, id-addressed command applied by a pure reducer. The browser editor, a Node agent, and a server pipeline all run this exact code:

import { applyCommand, type EditorCommand } from '@classytic/vixel-schema';

let next = applyCommand(spec, { type: 'splitClip', clipId: 'clip-3', atSec: 1.5 });
next = applyCommand(next, {
  type: 'setTransition',
  afterClipId: 'clip-3',
  ref: { id: 'zoom-punch', duration: 0.4 },
});

Commands targeting a missing id are a no-op (one stale ref can't crash a batch). New clips get fresh ids immediately, so the next command can address them. The with*() primitives behind the reducer (withClipSplit, withClipPatch, …) are exported too.

Catalog, packs, and BYO effects

import { describeCatalog, registerPack, registerSpecPacks } from '@classytic/vixel-schema';

describeCatalog(); // filters/effects/transitions/templates/themes — built for LLM context
registerPack(myPack); // a JSON manifest of effect/transition descriptors + GLSL/LUT URLs
registerSpecPacks(spec); // packs can travel INSIDE the spec — an agent can emit a novel shader

Renderers implement one generic executor per effect kind (filter / lut / overlay / shader); everything else is data — the gl-transitions / CapCut-pack model.

Bundling policy: data ships, assets are BYO

The convention that keeps vixel "batteries-included but lightweight":

  • Small pure-data descriptors ship as built-ins — effect / transition / text-preset metadata and inline GLSL, plus template builder functions. They are a few KB, cost nothing at runtime until a spec uses them, and being browsable is their value (an editor panel or agent enumerates the whole vocabulary). Add individual effects/transitions/presets straight to the built-in catalogs.
  • Larger or curated collections ship as opt-in packs — e.g. the essentials templates behind registerEssentialsTemplates(). Both @classytic/vixel-schema and @classytic/vixel-ui set sideEffects: false, so a pack a host never registers is tree-shaken out of its bundle entirely. Prefer this when there will be many entries or a host will want to curate which appear in a picker.
  • Binary assets are never bundled — particle textures, overlay footage (light leaks / dust), fonts, and audio SFX are brought by the host via a pack baseUrl (or an absolute asset URL on the descriptor). This keeps install size small and, just as importantly, keeps third-party asset licenses the host's explicit choice rather than something vixel embeds. A "confetti" preset ships as emitter data + a texture URL, not an embedded PNG.

When adding to vixel: reach for a built-in for one small descriptor, an opt-in pack for a collection, and always a URL (not an embed) for anything binary. See the repo NOTICE for third-party attributions this policy also governs.

Templates & themes

import { registerTheme, buildScene, applyCommand } from '@classytic/vixel-schema';

// A brand is just a registered Theme; templates read the resolved tokens.
const next = applyCommand(spec, {
  type: 'applyTemplate',
  template: 'studio/social-ad',
  theme: 'brand:acme',
  content: { headline: 'Launch day' },
});
// → a new layered scene with fillable slots (slot ids are addressable clip ids)

Timeline intelligence

  • Markers — timeline-absolute intent anchors (chapters, beats, notes) with VTT / FFMETADATA chapter export.
  • Link groups — A/V pairs share a linkId; the link-aware helpers answer "what moves with this?" for every editor.
  • RipplerippleDeleteRanges(spec, trackId, ranges): link-aware, multi-range, transcript-driven cuts.
  • TranscripttimelineTranscript(...) projects ASR words onto the output timeline (the "edit video like text" primitive).
  • Determinism — seeded PRNG + counter-based ids (./random): the same spec renders identically everywhere. Preview == export is designed in, not hoped for.

Subpath exports

| Subpath | Contents | | --- | --- | | @classytic/vixel-schema | the whole zero-dep contract + edit core + catalogs | | @classytic/vixel-schema/validate | parseSpec / safeParseSpec / validateSpec (pulls zod) | | @classytic/vixel-schema/isf | ISF shader → descriptor adapter |

License

MIT © Classytic

Trademark

MIT-licensed code. "Classytic"/"arc" names + logos are trademarks of Classytic LLC — see TRADEMARK.md.