BeatKhana Development

.bk.scene and .bk.component format

MessagePack package envelope, document graph, validation, dependencies and safe import/export.

Encoding and extension

.bk.scene and .bk.component files are binary MessagePack, served as application/x-msgpack; they are not JSON despite their object-shaped logical model. Version 2 packages include the root document plus every reusable component dependency reachable from it.

type OverlayPackageV2 = {
  format: 'bk.scene' | 'bk.component';
  version: 2;
  exportedAt: string;        // ISO-8601
  rootDocumentGuid: string;  // sourceGuid of the root
  documents: PackageDocumentRecord[];
};

type PackageDocumentRecord = {
  sourceGuid: string;
  document: {
    kind: 'scene' | 'component';
    name: string;
    widthPx?: number;
    heightPx?: number;
    sortOrder?: number;
    isDefault: false;
    publicProperties: Record<string, unknown>;
  };
  nodes: OverlayNode[];
};

Node model

A node has a unique guid, optional parentNodeGuid, nodeType (leaf or component_instance), optional built-in componentKind, optional sourceDocumentGuid, transform/layout fields, appearance fields, data bindings, media configuration and a free-form props object for component-specific settings.

Common transform fields are posX, posY, widthPx, heightPx, rotationDeg, scaleX, scaleY, originX, originY, opacity, zIndex, isVisible and isLocked. See Overlay editor for built-in kinds and inspector settings.

Dependency rules

  • rootDocumentGuid must resolve to one record.
  • The root kind must match the file extension/format.
  • Every non-root document must be a reusable component.
  • sourceGuid values are unique.
  • A v2 sourceDocumentGuid reference must resolve inside the package.
  • Component dependency cycles are rejected.
  • Node GUIDs are unique within a document.
  • Parent references must exist in the same document; self-parenting and hierarchy cycles are rejected.
  • Width and height, when supplied, must be finite and greater than zero.
  • publicProperties must be a plain object.

During import, source document and node GUIDs are remapped to new records so packages can coexist. Names/slugs are de-duplicated. A scene cannot be imported as a reusable dependency.

Legacy v1

The importer accepts a legacy v1/single-document shape with optional format, optional version: 1, document, and nodes. It lacks dependency packaging; unresolved reusable references may therefore be skipped. New tools should emit v2 only.

Decode example

import { decode } from '@msgpack/msgpack';
import { readFile } from 'node:fs/promises';

const bytes = await readFile('gameplay.bk.scene');
const pkg = decode(bytes);
if (pkg.version !== 2 || pkg.format !== 'bk.scene') {
  throw new Error('Unsupported BeatKhana scene package');
}
console.log(pkg.documents.length, pkg.rootDocumentGuid);

Do not edit binary packages with a text editor. Decode, validate, modify, re-encode, then test in a disposable overlay before importing into production.

On this page