The C++ API
Embedding Core
Prism Core is a C++20 library with no dependencies beyond the standard library. Download the prebuilt library + headers from Releases, add the include path, and link it — everything lives under the prism:: namespace with the prism/ include root:
#include "prism/Document.h"
#include "prism/Serialize.h"
#include "prism/Evaluate.h"
using namespace prism;
c++ -std=c++20 -Iprism-core/include app.cpp -Lprism-core/lib -lprism-core
The Document API
A Document is a value type — copyable, comparable, serializable. The ergonomic way to shape it is append, which adds a prim at a path and returns a chainable handle you set values on:
Document doc;
doc.append(Path("/world"), "group");
doc.append(Path("/world/mat"), "material").set("out", Float3(0.8, 0.1, 0.1));
doc.append(Path("/world/ball"), "object")
.set("radius", Float(1.5))
.set("albedo", Float3(1, 1, 1))
.setMeta("note", "hero object"); // string metadata, separate from typed properties
doc.connect(Path("/world/ball.albedo"), Path("/world/mat.out"));
doc.remove(Path("/world/old")); // removes the subtree
The handle re-resolves the prim by path on each call, so it stays valid across further edits. Values are constructed with the uniform helpers — Float, Float2, Float3, Int, Bool, Str — so the call site reads cleanly. For the raw prim, doc.prim(Path(...)) returns a Prim* (or nullptr).
Iterate prims and connections directly — they are plain data, not hidden behind a visitor:
for (const Prim& p : doc.prims()) /* p.path(), p.type() */;
for (const Connection& c : doc.connections()) /* c.target, c.source */;
Reading values safely
The chainable handle (from edit) carries permissive getters that return a default, and strict require* getters that fail loudly, so a consumer never silently gets the wrong shape:
float r = doc.edit(Path("/world/ball")).getFloat("radius", 1.0f); // permissive, with a default
Vec3 rgb = doc.edit(Path("/world/ball")).requireFloat3("albedo"); // throws a located error if absent/mistyped
To resolve through connections and time samples, use the evaluator rather than reading the stored default:
Value v = evaluate(doc, Path("/world/ball.albedo"), /*time*/ 12);
Value comparison is the basis of undo
Because a Document is a comparable value, a before/after pair is an edit. That is exactly how the Editor implements universal undo: snapshot, mutate, snapshot, push the pair onto a command bus. You do not need change-tracking machinery inside the document — the value semantics give it to you.
Document before = doc; // cheap value copy
mutate(doc);
bool changed = !(doc == before);
What lives where
Document.h / Prim.h / Path.h / Property.h / Value.h are the model; Serialize.h is the native ASCII and binary I/O; Evaluate.h is the evaluator and dependency graph; Compose.h is cross-file composition; Codec.h is the codec seam. To reach the model from another language, drop down to the C ABI.