Reactive Primitives
The reactive helpers in @zynthjs/skia are designed for time-based drawing and path updates that fit SolidJS signals. They expose clocks, path interpolation, and mutable path generation without requiring imperative render loops in user code.
These primitives are especially useful when Skia props should be driven by shared numeric values, animated progress, or procedurally generated geometry.
Basic usage
import {
Canvas,
Path,
createClock,
createPathInterpolation,
} from "@zynthjs/skia";
const clock = createClock();
const path = createPathInterpolation(
() => (Math.sin(clock()) + 1) / 2,
[0, 1],
["M 24 120 Q 160 48 296 120", "M 24 60 Q 160 132 296 60"],
);
export function ReactivePath() {
return (
<Canvas frameLoop clearColor="#020617" style={{ width: 320, height: 180 }}>
<Path path={path()} color="#38bdf8" style="stroke" strokeWidth={4} />
</Canvas>
);
}
Advanced examples
Clock-driven shader uniforms
import {
Canvas,
Rect,
Shader,
createClock,
createRuntimeEffect,
} from "@zynthjs/skia";
const clock = createClock({ durationMs: 10_000 });
const effect = createRuntimeEffect(`
uniform float t;
uniform vec2 size;
half4 main(vec2 fragCoord) {
vec2 uv = fragCoord / size;
return half4(uv.x, 0.4 + 0.2 * sin(t), uv.y, 1.0);
}
`);
if (!effect) {
throw new Error("Runtime shader effect could not be created.");
}
export function AnimatedShader() {
return (
<Canvas frameLoop style={{ width: 320, height: 180 }}>
<Rect x={0} y={0} width={320} height={180}>
<Shader source={effect} uniforms={{ t: clock(), size: [320, 180] }} />
</Rect>
</Canvas>
);
}
Procedural path values
import { Canvas, Path, createClock, createPathValue } from "@zynthjs/skia";
const clock = createClock();
const wave = createPathValue((path) => {
const t = clock();
path.moveTo(20, 90);
path.quadTo(110, 20 + Math.sin(t) * 30, 200, 90);
path.quadTo(255, 130 - Math.sin(t) * 20, 300, 90);
});
export function ProceduralPath() {
return (
<Canvas frameLoop clearColor="#111827" style={{ width: 320, height: 180 }}>
<Path path={wave()} color="#22c55e" style="stroke" strokeWidth={4} />
</Canvas>
);
}
Special cases and unusual features
createClock()returns seconds as an accessor.createPathInterpolation()requiresinputRange.length === outputRange.lengthand at least two range stops.- Interpolated paths must have the same command count and the same command types in each position.
createPathValue()accepts an optional initial path, then lets the updater mutate a fresh path object derived from that base.useClock,usePathInterpolation, andusePathValueare compatibility aliases for the same implementations.
API Reference
createClock(options?)
options?: { autoStart?: boolean; durationMs?: number; fallbackStepMs?: number }- returns
Accessor<number>
createPathInterpolation(progress, inputRange, outputRange)
progress: number | Accessor<number>inputRange: readonly number[]outputRange: readonly SkiaPathSource[]- returns
Accessor<SkiaPathObject>
createPathValue(updater, initialPath?)
updater: (path: SkiaPathObject) => voidinitialPath?: SkiaPathSource- returns
Accessor<SkiaPathObject>
Compatibility aliases
useClockusePathInterpolationusePathValue