GlyphfieldDocs
Documentation/System reference

Source format reference

Read and edit the JSON source behind each Studio tool, including the portable CanvasDocument, its validation rules, migrations and safe round trips.

Maintained with source

Studio source is the structured JSON state behind the active artifact. People edit it in the source drawer, which the Code button opens. Scripts and agents read and apply it through the Browser API with window.glyphfield.studio.readSource() and applySource().

The source stays available while its Studio tool is active, whether or not the source drawer is open. Brand book source holds only presentation edits (order, visibility, layout, surface, image and copy). Its content still comes from the Brand identity tool.

Rules for every tool

  • Source must parse as a JSON object. Arrays and primitive top-level values are rejected.
  • Each tool validates its own enums, ranges, references and required collections before it changes visible state.
  • When application fails, the current artifact stays unchanged and the error is reported.
  • A successful application updates the same state that the inspector, preview, persistence and export use.
  • Browser source is not a POST /api/generate request. Only a design-sequence response contains an apply-ready document, and that compact version-3 compatibility source is normalized to the current CanvasDocument when it is applied.
  • Keep unknown fields. Edit the current document instead of rebuilding an older example, so fields added by newer versions survive.

Portable CanvasDocument

Design Lab, the layered composition tool, stores its work in the shared CanvasDocument scene graph. The current canvas schema is schemaVersion: 2, and the Design Lab metadata records sourceVersion: 4.

{
  "schemaVersion": 2,
  "brandId": "gt",
  "capabilities": ["animation", "assets", "constraints", "guides", "history", "layers", "pages", "text"],
  "pageIds": ["<page id>"],
  "pages": {
    "<page id>": {
      "width": 1600,
      "height": 900,
      "background": "#070707",
      "elementIds": ["shader-canvas-1", "text-1", "logo-brand"]
    }
  },
  "elements": {},
  "assets": {},
  "metadata": {
    "tool": "design-lab",
    "designLab": {
      "sourceVersion": 4,
      "ratio": "wide",
      "exportSettings": {},
      "shaderSequence": {},
      "timeline": {},
      "layerShaders": {},
      "groups": [],
      "workspace": { "activeArtboardId": "artboard-main", "activeSurfaceId": "artboard-main", "artboards": [], "canvas": {} }
    }
  }
}

The complete document also carries id, title, timestamps, revision, guides and the element records. Keep them exactly as readSource() returns them.

Each element has bounds, constraints, hidden, locked, a style with opacity and blend mode, and tool-specific data that holds the full layer settings and the layerType. A text element keeps its text in content. Bounds x and y are positions. Bounds width and height are scale factors of the layer's default box. They are not pixel values. Bounds rotation is the layer's angle in degrees, clockwise about its center, from −180 to 180. The layer data mirrors a nonzero angle as transform.rotation, and bounds.rotation wins when the two differ. Documents saved before layers could rotate have no transform.rotation and load at 0. Effect elements always have rotation 0.

Image and logo elements reference asset records instead of browser-only paths. Identity assets use brand-asset:<library id>, and embedded files use resource:<layer id>. Layer IDs start with shader-, effect-, text-, logo- or asset-. Group IDs start with group-, and artboard IDs start with artboard-. metadata.designLab.identity holds the typography snapshot of an imported project.

Generated compatibility source

POST /api/generate with kind: "design-sequence" returns an HTTP envelope with schemaVersion: 1 and a compact document whose version is 3. That document uses the fields composition, ratio, timeline, shaderSequence and exportSettings. It is an input format and is not the current CanvasDocument. Apply it, then call readSource() to receive the normalized CanvasDocument shown above.

Design Lab layer data

LayerStored state
ShaderMaterial ID, colors, engine settings, native frame state, captured PNG reference, size, motion, transform, opacity, blend mode
TextText (content) with optional runs, type role (the role sets the family), weight, size, color, outline, shadow, text effect, shader fill, transform
LogoBrand or custom asset, color, appearance, shader fill, transform
ImageEmbedded or library asset, crop, focal point, zoom, fit, opacity, transform, appearance, and stickerFinish for stickers
EffectConverter kind (Bayer, ASCII, halftone or posterize) and its settings, foreground and background colors and opacity. An image effect stores effectId and effectProps instead

page.elementIds is the authoritative layer order from back to front. Groups are stored in the Design Lab metadata and must reference valid element IDs. When a shader layer is missing from the stored order, it is inserted behind the content. An authored order is kept.

Captured shader appearance

A shader application can carry frameState and frameSnapshot, which have different jobs:

  • frameState is the editable anchor of the native recipe: provider, floating-point engine time or frame, timeline time, material ID and any captured input that a provider needs. Current records are version 2, and older Paper version-1 anchors still load.
  • frameSnapshot is { version: 1, assetId, width, height } and references a lossless PNG in CanvasDocument.assets. Its ID has the form shader-frame:<sha256>, so repeated uses share the same bytes. Keep unknown snapshot fields when editing.

Shader layers keep these records in element data. Shader fills on content layers keep them in metadata.designLab.layerShaders. Workspace artboards keep their own snapshots. timeline.timeMs stores time separately from the older timeline.frame export frame index.

An ordinary pause can update frameState and the timeline without creating a PNG frameSnapshot. Playback, extending the time range, and PNG, JPG, GIF and MP4 export do not add captured-frame assets. Capturing an appearance is a separate, explicit operation.

Captured frames use durable local asset references instead of temporary blob: URLs. Portable JSON embeds the PNG bytes, and source application validates and imports them before it changes the composition. A missing frame produces an error that says what is missing, and the layer does not fall back to the default shader. A PNG keeps the captured appearance at its recorded resolution. It does not store an editable Fluid Energy simulation state, and the same procedural settings can render slightly differently on another GPU.

Tool document map

ToolSource documentValidation and compatibility
Brand identityComplete BrandIdentityChecked against the BrandIdentity JSON Schema from GET /api/brand/validate. Keeps the project id, kind, revision, builtIn and presetFingerprint and unknown fields. Asset and font paths must be data: URLs or paths on the Glyphfield origin. Agents merge targeted patches with identity.apply
Brand elementsSettings of the selected element (glyphfield.brand-element version 2, with elementId)Selects elementId and stores only values that differ from the identity defaults. Version 1 settings apply to the selected element
MoodboardBoards and tiles (glyphfield.moodboard version 2)The composition is showcase, campaign, system, specimen or catalog. Tiles validate variant, span, tone, image and copy. Version 1 still applies
Brand bookSections and pages (glyphfield.brand-book version 1)Section order and visibility, page visibility, layout, surface, image and copy (up to 1200 characters per slot). schema and version are checked. Foundations belong in the identity
AnimationCanvasDocument schema 2 with metadata.tool: "animation-studio" and metadata.animation (mode, textFrames, includeBrandLogo, sequenceOrder, settings, frameSettings, sequenceBackground, backgroundOverrides, playbackRate, audio, artboards, activeArtboardId, identity)playbackRate is greater than 0 and at most 4. Keyframes and text entrances are validated by path. Artboards and audio bytes are embedded
LottieCanvasDocument schema 2 with metadata.studio = { sourceVersion: 1, toolId: "lottie", state }. state holds the appearance, background, canvas preset, edits, playback and the source (raw JSON in source.data, or a .lottie asset on the lottie-animation element)Raw Lottie JSON with a numeric fr and a layers array can also be applied. It replaces the animation and clears the edits
Design LabCanvasDocument schema 2 with Design Lab source version 4Exact layers, transforms, assets, effects, sequences, saved frames and export settings
OpenGraph, Terminal card, Partnership lockup, Blog cover, Slide templatesCanvasDocument schema 2 with metadata.studio = { sourceVersion: 2, toolId, state }. Pages are artboards (each slide is a page), and elements are layers with a data.slot role and brand color rolesRejects another tool's document or a newer sourceVersion. Text layers need textStyle, and code layers need a tab. Library assets are pointed back to the identity's files. The mirrored flat fields title, body, code and language stay editable. Version-1 flat states migrate
Color tokens, TypographyFoundation state bound to the identity, version 2Color tokens: the palette, colorPairings, colorSystem (semantic light and dark roles, scale settings) and per-token opacity. Typography: roles, typeScale and specimen settings. Version 1 sources still apply, and font files stay local
Component libraryVersion 2: pattern, family, label, supportingCopy, disabled, size, density, radius, borderWidth, elevation, surface, theme, codeFormat, useBrandDefaults, colors, typography, textScale, letterSpacing, assetfamily must match the pattern. Version 1 migrates when applied

Safe agent edits

const studio = window.glyphfield.studio;
const before = JSON.parse(studio.readSource());

// Keep the envelope and change only the intended element.
const text = Object.values(before.elements)
  .find((element) => element.kind === 'text');
if (!text) throw new Error('No text element exists.');

text.content = 'Every language. One source.';
text.style.opacity = 0.92;

await studio.applySource(before);

const after = JSON.parse(studio.readSource());
if (after.elements[text.id].content !== text.content) {
  throw new Error('The change did not round-trip.');
}

Check describe().source before reading or applying. Do not write to localStorage, IndexedDB, component state or internal React properties directly.

Lottie validation

{
  "v": "5.12.2",
  "fr": 30,
  "ip": 0,
  "op": 90,
  "w": 1200,
  "h": 750,
  "layers": []
}

The browser player decides which Lottie features render. Recoloring, trimming and frame selection are Studio state. lottie.export.source and Export JSON write the updated vector document. An imported .lottie archive can be replayed and downloaded, but its layers, text and colors cannot be edited.

Portability checklist

  1. Read and keep the current schema and source versions.
  2. Keep asset records, page order, unknown metadata and stable element IDs.
  3. Apply through the tool validator and read the source again.
  4. Save the source or a named design when the work may need to be regenerated.
  5. Export the requested rendered format separately.
  6. Keep authorized font and asset files with the document when they are not embedded.

Source code editing, Studio Browser API and Formats and portability cover related tasks. Version compatibility explains how these independent version numbers relate and how migration works.

On this page