BBC's guide to development
  • General

    • About
    • Tools
    • Git(hub)
    • Showpad
    • Hosting
    • Maintenance
    • Security
    • Go live checklist
  • Front-end development

    • Bundlers
    • CSS/SCSS
    • Javascript
    • Vue
    • PHP
    • Mails
    • Dev Faq
  • Functions
  • Mixins
  • General

    • OOP Structure
  • Component Classes

    • Accordion
    • App
    • Component
    • HighwayApp
    • Popup
    • PNG Sequencer
    • Tab
  • Manager Classes

    • BountListenerMgr
    • Cache
    • Configuration
    • InViewStateMgr
    • Instance Manager
    • Event dispatcher
  • Factories

    • SwiperFactory
  • PDF

    • AssetLoader
    • BasePdfDoc
    • TemplatePdfDoc
    • CustomPdfDoc
  • Utility functions

    • canvas
    • Connection Status
    • css
    • dev
    • placeholder
    • dom
    • fetch
    • json
    • object
    • scroll
    • scrollbar
    • spreadsheets
    • string
    • url
  • General

    • ComponentMgr
    • ThreeJsViewer
  • Components

    • ComponentMgr
    • GltfModel
    • Snappable
    • Socket
    • ThreeJsViewer
    • ThreeJsViewerCamera
  • Loaders

    • ConfigurationSerializer
    • GltfBlockParser
  • Utils

    • CanvasInputAdapter
    • CollisionManager
    • SocketGridExpander
    • blender
    • headless
  • General

    • Troubleshooting
    • Legacy
  • Components

    • AssetBar
    • ConfigGenerator
    • ShowpadApp
  • Managers

    • Assets
    • AppsDb
    • Config
  • Utils

    • Connection Status
    • general
    • showpad-interactive
    • showpad-upload
  • Components

    • Accordion
    • BackButton
    • Breadcrumb
    • ByltButton
    • Hamburger
    • Icon
    • Logo
    • Loader
    • Modal
    • Popup
    • Prompt
    • ProgressBar
    • TextLoader
  • Composables

    • useDebugMode
    • useConnectionStatus
  • Utils

    • dom
    • props
  • General

    • General
    • Tracking
  • Components

    • Accordion
    • ActionButton
    • AssetItem
    • AssetList
    • BackButton
    • ConfigGenButton
    • Logo
    • Media
    • Modal
    • Popup
    • Prompt
    • SPButton
    • SPRouterView
    • SPTrackedRouterLink
    • TextLoader
    • View
  • Composables

    • useConnectionStatus
  • Stores

    • useAppsDbStore
    • useBreadcrumbStore
    • useShowpadAPIStore
    • useShowpadSDKStore
    • useSpConfigStore
    • useSpStore
    • useSpTrackingStore
  • The New Kit

    • General
    • Installation & Usage
    • ACF Blocks
    • PHPCS
    • Functions
    • Vite
    • WP Config
    • Staging Deployment
  • Best Practices

    • Page Structure
    • Fonts/Typography
  • Todo
GitHub
  • General

    • About
    • Tools
    • Git(hub)
    • Showpad
    • Hosting
    • Maintenance
    • Security
    • Go live checklist
  • Front-end development

    • Bundlers
    • CSS/SCSS
    • Javascript
    • Vue
    • PHP
    • Mails
    • Dev Faq
  • Functions
  • Mixins
  • General

    • OOP Structure
  • Component Classes

    • Accordion
    • App
    • Component
    • HighwayApp
    • Popup
    • PNG Sequencer
    • Tab
  • Manager Classes

    • BountListenerMgr
    • Cache
    • Configuration
    • InViewStateMgr
    • Instance Manager
    • Event dispatcher
  • Factories

    • SwiperFactory
  • PDF

    • AssetLoader
    • BasePdfDoc
    • TemplatePdfDoc
    • CustomPdfDoc
  • Utility functions

    • canvas
    • Connection Status
    • css
    • dev
    • placeholder
    • dom
    • fetch
    • json
    • object
    • scroll
    • scrollbar
    • spreadsheets
    • string
    • url
  • General

    • ComponentMgr
    • ThreeJsViewer
  • Components

    • ComponentMgr
    • GltfModel
    • Snappable
    • Socket
    • ThreeJsViewer
    • ThreeJsViewerCamera
  • Loaders

    • ConfigurationSerializer
    • GltfBlockParser
  • Utils

    • CanvasInputAdapter
    • CollisionManager
    • SocketGridExpander
    • blender
    • headless
  • General

    • Troubleshooting
    • Legacy
  • Components

    • AssetBar
    • ConfigGenerator
    • ShowpadApp
  • Managers

    • Assets
    • AppsDb
    • Config
  • Utils

    • Connection Status
    • general
    • showpad-interactive
    • showpad-upload
  • Components

    • Accordion
    • BackButton
    • Breadcrumb
    • ByltButton
    • Hamburger
    • Icon
    • Logo
    • Loader
    • Modal
    • Popup
    • Prompt
    • ProgressBar
    • TextLoader
  • Composables

    • useDebugMode
    • useConnectionStatus
  • Utils

    • dom
    • props
  • General

    • General
    • Tracking
  • Components

    • Accordion
    • ActionButton
    • AssetItem
    • AssetList
    • BackButton
    • ConfigGenButton
    • Logo
    • Media
    • Modal
    • Popup
    • Prompt
    • SPButton
    • SPRouterView
    • SPTrackedRouterLink
    • TextLoader
    • View
  • Composables

    • useConnectionStatus
  • Stores

    • useAppsDbStore
    • useBreadcrumbStore
    • useShowpadAPIStore
    • useShowpadSDKStore
    • useSpConfigStore
    • useSpStore
    • useSpTrackingStore
  • The New Kit

    • General
    • Installation & Usage
    • ACF Blocks
    • PHPCS
    • Functions
    • Vite
    • WP Config
    • Staging Deployment
  • Best Practices

    • Page Structure
    • Fonts/Typography
  • Todo
GitHub
  • ConfigurationSerializer

ConfigurationSerializer

Serialises and restores ComponentMgr block configurations to and from a portable JSON structure. All methods are static. No DOM dependency.

Warning

Meant to be used with ComponentMgr only.

Getting started

import { ConfigurationSerializer } from './configurator';

// Save current scene
const config = ConfigurationSerializer.serializePlacedBlocks(componentMgr);
localStorage.setItem('config', JSON.stringify({ blocks: config }));

// Restore a scene
const saved = JSON.parse(localStorage.getItem('config'));
const { placed, skipped } = ConfigurationSerializer.restorePlacedBlocks(
  saved,
  componentMgr,
  blocks,           // Map<blockId, Snappable> from GltfBlockParser.parse()
  missingBlockIds,  // optional — IDs to skip gracefully
);
console.log(`Restored: ${placed} blocks, skipped: ${skipped}`);

Methods

ConfigurationSerializer.serializePlacedBlocks(componentMgr) → BlockData[]

Snapshots all placed blocks from componentMgr.objects into a serialisable array.

  • Parameters: componentMgr — ComponentMgr
  • Returns: Array of plain objects (one per placed block)

Each object has the shape:

{
  blockId: string,           // e.g. "leg-short"
  parentBlockId: string | null,  // parent block's ID, or null for root blocks
  socketLocks: [
    {
      socketIndex: number,              // index in this block's socket array
      lockedPartnerBlockId: string,     // partner block's ID
      lockedPartnerSocketIndex: number, // index in partner block's socket array
    }
  ],
  rotationSteps: number,     // 0–7 (normalised to 8-step range)
  rotationDegrees: number,   // rotationSteps × 45
}

ConfigurationSerializer.restorePlacedBlocks(config, componentMgr, blocksMap, missingBlockIds?) → { placed, skipped }

Clears the current scene and restores blocks from a saved configuration.

  • Parameters:
    • config — object with a blocks array (as returned by serializePlacedBlocks, wrapped in { blocks: [...] })
    • componentMgr — ComponentMgr — will be cleared and repopulated
    • blocksMap — Map<string, Snappable> — the template map from GltfBlockParser.parse()
    • missingBlockIds — string[], optional — block IDs that are known to be absent; they and their dependants are skipped gracefully
  • Returns: { placed: number, skipped: number }

Restoration order:

  1. All existing blocks are removed from componentMgr
  2. Block instances are created from templates (one clone per entry in config.blocks)
  3. Blocks are sorted topologically — parents before children — so that when a child block is placed, its parent already exists in the scene. The sort works iteratively: each pass places any block whose parentBlockId is either null or already in the placed set. This handles arbitrary tree depths without recursion.
  4. Socket locks are restored and snapItemToSocket is called to position each child block at the correct snap location relative to its parent socket
  5. Circular references and missing parents are detected and handled without throwing — see details below

Handling cycles and missing parents:

  • Circular reference (A is parent of B, B is parent of A): the topological sort detects that neither block can be placed first (both are waiting on the other). When a pass completes without placing any block, the deadlocked remainder is placed as-is with a console.warn, and parentBlock is set to null on any block involved in a cycle.
  • Missing parent (parent's blockId not found in blocksMap or listed in missingBlockIds): the child is treated as a root block — parentBlock = null — and placed without snapping. It appears at the world origin.

Notes

  • rotationSteps is normalised to [0, 7] (8 steps × 45° each) on serialise. On restore it is converted back to radians.
  • If a blockId in the config is not found in blocksMap, the block is counted as skipped. Pass known-missing IDs in missingBlockIds to suppress console.warn output for IDs you already know are absent (e.g. a block type that was removed from the GLTF in a newer version). Any block whose parentBlockId is in the missing set is also skipped, since it cannot be positioned without its parent.
  • Primary socket lock: each block in the config records all its locked socket pairs in socketLocks. The first entry (socketLocks[0]) is the "primary" lock — it is the one used to call snapItemToSocket and physically position the block. Any additional locks (e.g. a block that straddles two sockets on the same parent) are restored as lock-state-only: the lockedPartner references are set on both sockets, but no position update is applied. This is acceptable because a block can only be positioned relative to one anchor at a time; the extra locks are informational. The primary lock is always the first socket in iteration order that was found to be locked at serialise time.
  • missingBlockIds is provided as a separate parameter (rather than inferred from blocksMap) because there are scenarios where a block template exists in blocksMap but you still want to skip specific instances — for example, when restoring a partial configuration or when a block has been marked as deleted in your application layer.
Edit this page
Last Updated: 4/27/26, 12:56 PM
Contributors: Nicolas Jaenen