Quickstart
Quickstart
Miraiclip is pre-1.0 — expect API changes between minor versions.
@miraiclip/core and @miraiclip/renderer are published on npm.Installation
npm install @miraiclip/core
# for in-browser playback and preview, add the renderer:
npm install @miraiclip/rendererThe core is headless (browser + Node); the renderer is browser-only (WebCodecs, WebGL, Web Audio).
Create a project
The project state is a Zustand store — the single source of truth for your composition.
import { createProject } from "@miraiclip/core";
const project = createProject({ width: 1920, height: 1080, fps: 30 });Add media, tracks, and clips
Everything is a command — deterministic, undoable, serializable.
// Register a media asset (metadata only; core does no decoding)
project.dispatch({
type: "asset/add",
payload: { id: "intro", kind: "video", src: "/media/intro.mp4", durationUs: 12_000_000 },
});
project.dispatch({ type: "track/add", payload: { id: "video-1", kind: "video" } });
project.dispatch({
type: "clip/add",
payload: {
kind: "video",
id: "clip-1",
trackId: "video-1",
assetId: "intro",
startUs: 0, // timeline position in microseconds
durationUs: 5_000_000, // 5 seconds
},
});Transactions and time travel
Batch commands into one undoable history entry:
project.transaction(() => {
project.dispatch({
type: "clip/split",
payload: { clipId: "clip-1", atUs: 2_000_000, newClipId: "clip-1b" },
});
project.dispatch({ type: "clip/move", payload: { clipId: "clip-1", startUs: 1_000_000 } });
});
project.undo();
project.redo();React to changes
State changes are emitted as granular patches — the substrate for sync and collaboration:
project.events.on("patches", ({ patches, inverse, source }) => {
console.log(patches);
// e.g. [{ op: "replace", path: "/clips/clip-1/startUs", value: 1000000 }]
});Read state and move the playhead
// The composition document lives under state.doc
const { tracks, clips, trackOrder } = project.getState().doc;
// Playhead and selection are ephemeral: not undoable, not serialized
project.setPlayhead(1_500_000);
project.subscribe(
(s) => s.playheadUs,
(playheadUs) => console.log("playhead moved", playheadUs),
);Save and load
const saved = project.toJSON();
const restored = createProject(saved);Play it back
Everything above is headless. To turn the project into pixels and sound, hand it to the renderer’s createPlayer — one facade wiring the decode pipeline, compositor, and audio engine to a canvas. See the Rendering guide for the full setup:
import { createPlayer } from "@miraiclip/renderer";
const player = createPlayer(project, {/* backend, demuxer, decoder, audio */});
player.play();