Skip to main content

Studio SDK

View on GitHub npm version

A JavaScript SDK for browser-based video editing with timeline, canvas preview, and export. It provides a fully interactive editing environment that works with the Shotstack data model.

Interactive Examples

Try the Shotstack Studio in your preferred framework:

TypeScript React Vue Angular Next.js

Installation

npm install @shotstack/shotstack-studio
yarn add @shotstack/shotstack-studio

Quick Start

import { Edit, Canvas, Controls, Timeline, UIController } from "@shotstack/shotstack-studio";

// 1) Load a template
const response = await fetch("https://shotstack-assets.s3.amazonaws.com/templates/hello-world/hello.json");
const template = await response.json();

// 2) Create core components
const edit = new Edit(template);
const canvas = new Canvas(edit);
const ui = UIController.create(edit, canvas);

// 3) Load canvas and edit
await canvas.load();
await edit.load();

// 4) Register toolbar buttons
ui.registerButton({
id: "text",
icon: `<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3H13"/><path d="M8 3V13"/><path d="M5 13H11"/></svg>`,
tooltip: "Add Text"
});

// 5) Handle button clicks
ui.on("button:text", ({ position }) => {
edit.addTrack(0, {
clips: [
{
asset: {
type: "rich-text",
text: "Title",
font: { family: "Work Sans", size: 72, weight: 600, color: "#ffffff", opacity: 1 },
align: { horizontal: "center", vertical: "middle" }
},
start: position,
length: 5,
width: 500,
height: 200
}
]
});
});

// 6) Initialize the Timeline
const timelineContainer = document.querySelector("[data-shotstack-timeline]") as HTMLElement;
const timeline = new Timeline(edit, timelineContainer);
await timeline.load();

// 7) Add keyboard controls
const controls = new Controls(edit);
await controls.load();

// 8) Add event handlers
edit.events.on("clip:selected", data => {
console.log("Clip selected:", data);
});

Your HTML must include both containers:

<div data-shotstack-studio></div>
<div data-shotstack-timeline></div>

Main Components

Edit

Edit is the runtime editing session and source of truth for document mutations.

import { Edit } from "@shotstack/shotstack-studio";

const edit = new Edit(templateJson);
await edit.load();

await edit.loadEdit(nextTemplateJson);

// Playback (seconds)
edit.play();
edit.pause();
edit.seek(2);
edit.stop();

// Mutations
await edit.addTrack(0, { clips: [] });
await edit.addClip(0, {
asset: { type: "image", src: "https://example.com/image.jpg" },
start: 0,
length: 5
});
await edit.updateClip(0, 0, { length: 6 });
await edit.deleteClip(0, 0);

// History
await edit.undo();
await edit.redo();

// Clip operations
await edit.deleteTrack(0);

// Output settings
await edit.setOutputSize(1920, 1080);
await edit.setOutputFps(30);
await edit.setOutputFormat("mp4");
await edit.setOutputResolution("hd");
await edit.setOutputAspectRatio("16:9");
await edit.setTimelineBackground("#000000");

// Read state
const time = edit.playbackTime;
const playing = edit.isPlaying;
const clip = edit.getClip(0, 0);
const track = edit.getTrack(0);
const snapshot = edit.getEdit();
const durationSeconds = edit.totalDuration;

Events

Listen using string event names:

const unsubscribeClipSelected = edit.events.on("clip:selected", data => {
console.log("Selected clip", data.trackIndex, data.clipIndex);
});

edit.events.on("clip:updated", data => {
console.log("Updated from", data.previous, "to", data.current);
});

edit.events.on("playback:play", () => {
console.log("Playback started");
});

// Unsubscribe when no longer needed
unsubscribeClipSelected();

Available event names:

CategoryEvent Names
Playbackplayback:play, playback:pause
Timelinetimeline:updated, timeline:backgroundChanged
Clip lifecycleclip:added, clip:selected, clip:updated, clip:deleted, clip:restored, clip:copied, clip:loadFailed, clip:unresolved
Selectionselection:cleared
Edit stateedit:changed, edit:undo, edit:redo
Tracktrack:added, track:removed
Durationduration:changed
Outputoutput:resized, output:resolutionChanged, output:aspectRatioChanged, output:fpsChanged, output:formatChanged, output:destinationsChanged
Merge fieldsmergefield:changed

Canvas

Canvas renders the current edit.

import { Canvas } from "@shotstack/shotstack-studio";

const canvas = new Canvas(edit);
await canvas.load();

canvas.centerEdit();
canvas.zoomToFit();
canvas.setZoom(1.25);
canvas.resize();
const zoom = canvas.getZoom();
canvas.dispose();

UIController

UIController manages built-in UI wiring and extensible button events.

import { UIController } from "@shotstack/shotstack-studio";

const ui = UIController.create(edit, canvas, { mergeFields: true });

ui.registerButton({
id: "add-title",
icon: `<svg viewBox="0 0 16 16">...</svg>`,
tooltip: "Add Title"
});

const unsubscribe = ui.on("button:add-title", ({ position }) => {
console.log("Button clicked at", position, "seconds");
});

ui.unregisterButton("add-title");
unsubscribe();
ui.dispose();

Timeline

Timeline provides visual clip editing.

import { Timeline } from "@shotstack/shotstack-studio";

const container = document.querySelector("[data-shotstack-timeline]") as HTMLElement;
const timeline = new Timeline(edit, container);

await timeline.load();
timeline.zoomIn();
timeline.zoomOut();
timeline.dispose();

Controls

Controls enables keyboard playback and edit shortcuts.

import { Controls } from "@shotstack/shotstack-studio";

const controls = new Controls(edit);
await controls.load();

Available keyboard shortcuts:

KeyAction
SpacePlay / Pause
JStop
KPause
LPlay
Left ArrowSeek backward
Right ArrowSeek forward
Shift + ArrowSeek larger amount
CommaStep backward one frame
PeriodStep forward one frame

VideoExporter

VideoExporter exports a timeline render from the browser runtime.

import { VideoExporter } from "@shotstack/shotstack-studio";

const exporter = new VideoExporter(edit, canvas);
await exporter.export("my-video.mp4", 25);

Custom UI Buttons

Use UIController to register and handle custom button actions.

ui.registerButton({
id: "text",
icon: `<svg viewBox="0 0 16 16">...</svg>`,
tooltip: "Add Text",
dividerBefore: true
});

ui.on("button:text", ({ position, selectedClip }) => {
console.log("Current time (seconds):", position);
console.log("Current selection:", selectedClip);
});

ui.unregisterButton("text");

API Reference

For schema-level details and type definitions, see the Shotstack API Reference.