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

@pixodesk/svg-animator-core

v1.0.43

Published

Pixodesk SVG animator core — schema, document types, materializers and sampling shared by all platform players

Downloads

1,210

Readme

animator-core

📖 Full user guide: docs/format/core-library.md · all docs

CI License: MIT

Platform-neutral core of the Pixodesk SVG animator: the document schema, the effect materializers, the interpolation engine and the path sampler — with no DOM dependency at all. It is what every player shares, so the web player and the React Native player produce identical values from the same document.

🚧 Status - This project is currently under development.

Do I need this package?

Usually no. If you just want to play an animation, install a player:

| You are building for | Install | |---|---| | Browser (vanilla JS) | @pixodesk/svg-animator-web | | React | @pixodesk/svg-animator-react | | Vue | @pixodesk/svg-animator-vue | | React Native | @pixodesk/svg-animator-rn 🧪 |

Each of those depends on this package and re-exports what you need.

Install it directly when you want to work with documents rather than play them — validating them, transforming them, flattening them for a renderer of your own, or computing values at a given time without rendering anything.

npm install @pixodesk/svg-animator-core

Why it exists

A player has to answer two very different questions:

  1. What should be on screen at time t? — schema, effects, easing, interpolation, path sampling. Pure computation, identical on every platform.
  2. How do I put it there? — DOM elements, WAAPI, react-native-svg. Platform-specific.

Everything in category 1 lives here. The package compiles without the TypeScript dom library, so a stray document reference is a build error rather than a runtime crash on a non-browser platform.

What's inside

| Area | Exports | |---|---| | Schema & types | PxAnimatedSvgDocumentSchema, PxNodeSchema, PxEffectsSchema, … plus every Px* TypeScript type and the px schema builder | | Validation | validateDocument (the whole document, strict), isPxDocument, isValidPxDocument, validateNodeEffects | | Materializers | materializeAllInTree, materializeNodeEffects | | Interpolation | calcAnimationValues, interpolateValue, normalizeBindings | | Sampling / geometry | createPathSampler, bezier helpers, cubicBezier, splitEasing | | Text | materializeGlyphText, layoutGlyphTextChars, extendedPathForBrowser | | Node helpers | toDomProps, sanitizeAttributeValue, generateNewIds | | Playback engine | createAdapterAnimator + the PxPlatformAdapter interface | | Wire enums | PxTimelineEngine / PxTimelineEngineSetting, PxStartOn, PxOutAction, PxFinishAction, PxFillMode, PxPlaybackDirection, PxScrollKind, PxScrollAxis, PxScrollSource, PxScrollPhase, PxPinAlign, PxAlongPathMode, PxLoopRepeatAt, PxLoopDirection, PxStrokeTrimSubPaths, PxMaskType, PxCloneWithout, PxUnits, PxGradientType, PxGradientSpreadMethod, PxPathOverflow, PxLengthAdjust, PxTextPathMethod, PxTextPathSpacing — every two-or-more-way wire selector is a named enum, not a bare string. Each is a const namespace AND the string type derived from it under the same name, so PxStartOn.click and startOn?: PxStartOn come from one import |

Validating a document

isPxDocument(json) is the cheap shallow gate (is this a Px document at all?); isValidPxDocument(json) runs the full schema. For per-field diagnostics, call a schema's isValid with a context:

import { PxAnimatedSvgDocumentSchema, type PxValidationContext } from '@pixodesk/svg-animator-core';

const ctx: PxValidationContext = { errors: [], warnings: [], strict: true };
const ok = PxAnimatedSvgDocumentSchema.isValid(doc, ctx, []);
if (!ok) console.error(ctx.errors);   // ["children[0].effects.strokeTrim.range: …", …]

Two modes, two different questions:

| mode | question it answers | undeclared keys | |---|---|---| | default (strict absent/false) | is this document repairable? — what sanitize would accept | ignored, so unknown future fields stay forward-compatible | | strict: true | is this document well-formed? — the wire shape locked to its schema | reported as errors on closed objects |

Use default in production readers and strict in tests and tooling. Two notes on strict, both deliberate: it reaches inside unions (a union member is checked in the caller's mode, though its per-branch errors are not reported unless every branch fails), and it ignores keys whose value is undefined — those cannot survive JSON.stringify, so strict judges the document rather than the in-memory object that produced it.

The schema the format is written in

Nothing above is validated by hand. Every block of the format has a runtime schema built with the px schema builder, and validateDocument walks those. You can walk them too: describeSchema turns any schema into a plain description — keys, types, and whether each is optional — which is how this repo generates its published SCHEMA.json, and schemaKeys lists just the keys.

There is one schema value per block, named after it: PxTriggerSchema, PxElementAnimationSchema, PxKeyframeValueSchema, PxAttrValueSchema, PxTransformValueSchema, PxBezierPathSchema, PxScrollSchema, PxScrollRangeSchema, PxScrollRangePointSchema, PxRetimeEffectSchema and PxGradientStopSchema among them. Two hold a node's shared halves rather than a block of their own — PxNodeBaseSchema is what every node has, PxSvgNodeRootSchema what only the root <svg> adds.

To type a schema, or build one of your own: PxSchema is the schema type itself, PxSchemaDesc what describeSchema hands back, PxInfer the document type a schema describes, and PxRemoveIndex strips the index signature that SVG pass-through keys bring with them.

Reading and reshaping a document

diagnoseDocument is the load-time check every player runs before it builds anything: it hands back the problems it found instead of throwing, and validateDocument above is the fuller form of the same question.

A document's timeline has two shapes — the nested object the file stores, and the flat view the engines read. flattenAnimatorTimeline and nestAnimatorTimeline convert between them, so an editor can hold one and a player the other with neither having to guess. PX_TRANSFORM_PART_KEYS lists the parts a transform is written in, in the order they compose.

The materialization pipeline

materializeAllInTree(doc, engine) is the single entry point that turns a lightweight editor document into a flat tree any renderer can walk:

  1. Effectsnode.effects (transformBy, repeater, maskedBy, strokeTrim, clone/retime, gradients, textPath) become real nodes, wrappers and defs.
  2. Loops — each property's loop is expanded into explicit keyframes.
  3. Motion paths — tangented transform keyframes plus autoOrient are sampled into plain {translate, rotate} keyframes.
  4. Animated <use> — replaced by <g> + a deep clone with fresh ids.

Steps 3 and 4 run when engine is native. Pass native for any renderer without live <use> propagation — that includes react-native-svg — and js only for the DOM, which resolves <use> references natively.

import {
    materializeAllInTree, generateNewIds, calcAnimationValues,
    normalizeBindings, PxTimelineEngine,
} from '@pixodesk/svg-animator-core';

// Flatten once …
const flat = generateNewIds(materializeAllInTree(doc, PxTimelineEngine.native));

// … then ask for values at any time, with no renderer involved.
for (const binding of normalizeBindings(flat, PxTimelineEngine.js) ?? []) {
    const values = calcAnimationValues(binding.animate, 500); // t = 500 ms
    console.log(binding.id, values);   // → { opacity: '0.5', transform: 'translate(…)' }
}

This is exactly how the React Native player precomputes its animation tracks, and how the frame-loop engine renders each tick in the browser — same function, same numbers.

Writing your own player

Implement PxPlatformAdapter and hand it to createAdapterAnimator; the engine handles timing, delay, direction, iterations, fill, playback rate and the lifecycle callbacks, then calls you with plain attribute writes.

import { createAdapterAnimator, type PxPlatformAdapter } from '@pixodesk/svg-animator-core';

const adapter: PxPlatformAdapter = {
    isConnected: () => true,
    setAttribute: (id, attrName, value) => { /* apply to your element */ },
};

const api = createAdapterAnimator(flatDoc, adapter, {
    onFinish: () => console.log('done'),
});
api.play();

Frame scheduling resolves requestAnimationFrame from globalThis at call time and falls back to setTimeout, so the engine works in browsers, React Native and test environments with faked timers.

Every player agrees on one meaning of time, and the helpers that define it are exported so a player of your own cannot drift from it. seekCeilingMs is the highest time you can seek to and progressSpanMs the span a 01 progress maps onto; clampSeekMs holds a seek inside that span, and timeToProgress / progressToTimeMs convert between the two. isValidPlaybackRate says whether a rate can be used, and PX_RATE_REJECTED is what a setter reports when it cannot. createRunClock is the clock the frame loop itself runs on. The callbacks that engine accepts are PxEngineCallbacks — the playback lifecycle plus the diagnostics channel, and the shape each player's own callback type is built on.

Versioning

Every package in this repo is released in lockstep. A player depends on the matching core version (^x.y.z), so upgrading a player upgrades the core with it.

A document carries its own version, which moves independently of the package's: animator.version, stored under the key PX_WIRE_VERSION_KEY and parsed into a PxWireVersion. PX_WIRE_VERSION is the version this build writes and PX_WIRE_BASELINE_VERSION the oldest it still reads; PX_WIRE_STEPS is the ordered list of conversions between them, each a PxWireVersionStep of some PxWireStepKind. convertWireDocument brings a document up to this build and reports what it did in a PxWireConversionResult. applyWireSteps runs a chosen subset of those steps instead, taking a PxWireConversionOptions — which is what the release tooling uses.

License

MIT © Pixodesk