Prism: the substrate everything sits on
The document model
Every layer rests on one data model. A Document is a set of typed Elements at hierarchical Paths, wired by Connections, varying over time, composable across files. Scenes, images, documents, and view trees are all the same model with different type tokens.
One model under everything
The substrate is Prism, implemented in Kinogaki Core. It is general on purpose: a 3-D scene, a raster image, a vector drawing, a prose document, and a tree of UI widgets are the same structure with different type tokens and properties. Learn it once and it holds for every layer above.
A Document is a set of Elements indexed by Path, each carrying typed Propertys; a Property is a Value, optionally varying over time.
The five concepts
- Document. The source of truth and the unit of I/O. A value type: copyable, comparable, serializable. It holds an ordered set of Elements plus the list of Connections. Value-equality is the basis of undo (snapshot, mutate, compare). One Document is one file.
- Element. A typed node at a Path. It carries named Properties and string metadata. The type token (
material,object,issue,column) says what kind of node it is; Core does not privilege any token. - Path. The identity.
/world/ballis both where the Element sits in the hierarchy and its name. There are no UUIDs anywhere. Hierarchy is implicit in the Path, so cycles are impossible and a reference is just a Path. This single decision is what makes reconciliation and references cheap (see the wire). - Property. A named Value on an Element, optionally with time samples. Animation is not a subsystem; it is an axis on every numeric Property.
resolve(t)interpolates. - Value. One type for every datum: an element dtype (bool, ints, floats, string) plus a shape (scalar, 1-D, N-D) over a flat buffer. A float3, a matrix, a color, an image, and a point array are all just (dtype, shape).
Connections: the Document is also a graph
A Property can connect to another Element's output slot: connect(source_output, target_input). One source drives one input. This is the single reference mechanism (bindings, node links, value-to-slot wiring), so the Document is a dataflow graph as well as a tree. Rename, reparent, or remove an Element and every affected Path and connection endpoint is rewritten, so wiring never dangles. Evaluating a slot pulls its value through the connections and time samples, memoized and cycle-safe.
Composition: files that point at each other
Across files, an Element can reference another Document (a reserved reference metadata Path, optionally with a referencePath). Composing reads them as one flattened Document, grafting the referenced subtree under the referencing Element with its connections. Local opinions win: the referencing Document overrides what the reference supplies. References resolve recursively, the reference graph is memoized rather than re-walked, and cycles are rejected. Model an asset once, reference it many times, fix it in one place.
Codecs: read and written by anything
Foreign formats are codecs behind a stable Codec seam in Core: JSON, Markdown, HTML, and SVG today. Each targets a canonical model shared by every codec of its kind, so a conversion has a defined round-trip rather than a lossy guess. Prism sits in the middle as the neutral form. (These docs are themselves Markdown converted to Prism and rendered to HTML by that path; see the docs topology.)
Serialization: two encodings, one model
A Document writes to two interchangeable forms, detected by content (magic bytes), not by file suffix:
.prismais a USDA-style text form: human-authored, diff-clean, the source you read and review..prismis the compact binary form (a string table plus flat element, property, and connection blocks), optionally LZ-compressed for large array payloads.
The bytes the model serializes are exactly the bytes that travel on the wire. That is why the server-driven-UI loop needs no separate message format: a view, an edit, and a diff are all just Documents.
Why this matters for the rest
Because identity is a Path and a patch is an overlay, the layers above get reconciliation and references for free. Because a Value is just (dtype, shape), the same serializer handles a UI tree and a point cloud. The model is the reason the architecture stays small. Next: the libraries that implement it.