The packages
Five packages on npm. One reads and writes stories, one draws them, and the rest matter only if you are building a host that executes them.
This website uses the same published packages you would — nothing here runs against a private build.
@openverb/geostorystart hereThe geographic profile. Parse, validate, serialise, compile.
@openverb/motionnewDraws a document. Frames, video, and a linter.
@openverb/formatThe .ov document format itself, and the bridge to the runtime.
@openverb/runtimeExecutes verbs. Handlers and adapters.
@openverb/sdkClient for calling a runtime from a browser or a server.
@openverb/geostory
The one most people want. It reads and writes .ov files in the geostory profile, validates them against the JSON Schema, and compiles scenes into verb actions.
npm install @openverb/geostoryimport {
parseGeoStory,
validateGeoStory,
serializeGeoStory,
compileTimeline,
} from "@openverb/geostory"
const doc = parseGeoStory(await fs.readFile("story.ov", "utf8"))
const { valid, errors } = validateGeoStory(doc)
if (!valid) throw new Error(errors.join("\n"))
// What a renderer actually plays
const timeline = compileTimeline(doc)
// Back out again, unknown fields intact
await fs.writeFile("story.ov", serializeGeoStory(doc))| Export | What it does |
|---|---|
parseGeoStory(text) | YAML or JSON to a typed GeoStoryDocument. Throws GeoStoryParseError. |
parseGeoStoryRaw(text) | The same, without profile validation. |
serializeGeoStory(doc) | Back to YAML, preserving unknown fields. |
serializeGeoStoryToYAML(doc) | Explicit YAML. |
serializeGeoStoryToJSON(doc) | Explicit JSON. |
validateGeoStory(doc) | { valid, errors } against the JSON Schema. |
isGeoStoryDocument(value) | Type guard. |
compileToOVDocument(doc) | Scenes to a full OVDocument of verb actions. |
compileTimeline(doc) | Just the flattened action timeline. |
compileScene(scene, doc) | One scene's actions. |
GEOSTORY_SCHEMA | The JSON Schema object. |
verbLibrary | The verb definitions this profile emits. |
VERB_CATEGORIES | Verb category names. |
GeoStoryParseError | Thrown by the parsers. |
Types are exported for every part of a document — GeoStoryDocument, GeoStoryScene, GeoStoryPlace, GeoStoryCamera, GeoStoryTiming and the rest.
One thing to know if you are writing a tool: the serializer preserves fields it does not recognise, and that is a guarantee you can rely on. Do not round-trip a document through JSON.parse / JSON.stringify of your own model — you will silently drop whatever your model does not represent, which is the one thing the format promises never happens.
@openverb/motion
The other half of the pair. geostory tells you whether a document is well-formed; this one puts it on a canvas. Nothing in it parses, which is the point — a host can adopt the format without adopting a renderer, or write its own and still read everybody's documents.
npm install @openverb/motion| Export | What it does |
|---|---|
renderFrame | A document and a time in, a drawn canvas out. The one most hosts want. |
exportVideo | A run of frames into an MP4. Needs WebCodecs and the optional mediabunny peer. |
canExport | Whether this browser can encode, so you can decide before offering the button. |
totalDuration | How long the document runs, including audio that outlasts the last scene. |
sceneWindows | Every scene with its absolute start and end. |
drawAnnotations / drawFlow / drawChart | The drawing layer, for a host that owns its own frame loop. |
canvasProjector / canvasBox | Percentages of the frame, and the 16:9 box they are read against. |
lintGeoStory | Faults a document can have while parsing and validating perfectly. |
The timing, geometry and linting halves are pure and run under Node, so a document can be checked in CI without a display. renderFrame needs a canvas and exportVideo needs WebCodecs.
Motion is not a second format. A document whose annotations are addressed in canvas space, with no basemap, is still a GeoStory document and still says profile: geostory. Motion is what you get when the geography is absent: the same renderer and the same primitives, pointed at a frame instead of at the ground.
@openverb/format
The .ov format underneath the profile. You need this directly only if you are working with OpenVerb documents that are not geostories.
npm install @openverb/format| Export | What it does |
|---|---|
parseOV(yaml) | Parses any .ov document into an OVScript. |
bridgeToRuntime(script, options) | Turns a script into ExecuteRequests for a runtime. |
Note for browser work: this package reaches Node’s fs, so it cannot be bundled into client code — importing it from a React component fails the build with Can’t resolve 'fs'. Types are fine, since they are erased before bundling. Anything that is a value at runtime has to stay on the server. This site parses and serialises through API routes for exactly that reason.
@openverb/runtime
Executes verbs. Three concepts: verbs are the vocabulary, handlers are the functions that carry them out, and adapters connect the runtime to whatever is doing the work.
npm install @openverb/runtime| Export | What it does |
|---|---|
createRuntime(config) | Builds a runtime. |
loadVerbs(directory) | Loads verb definitions from disk. |
runtime.execute(request) | Runs one request. |
@openverb/sdk
Calls a runtime from a client. Ships an OpenVerbClient plus React hooks — useVerb and useVerbMutation.
npm install @openverb/sdkYou may not need any of this
A story is a text file. If all you want is to read one, you can parse the YAML with any library and walk the registries — the document is designed to be legible. The packages matter when you want validation, the unknown-field guarantee, and the compiled action timeline without writing them yourself.