Skip to content

Layout System

The layout system is the bridge between abstract visual actions and concrete rendering on screen. A layout defines how algorithm data is spatially arranged, which visual primitives are used, and how visual actions translate to visual changes.


A layout is a rendering strategy. It determines:

  • How the algorithm’s state is spatially arranged (e.g., array cells in a row, graph nodes in a force-directed layout, a heatmap grid).
  • Which visual primitives (elements, connections, containers, annotations, overlays) are available.
  • How each visual action type maps to changes in those primitives.
  • What layout-specific configuration is read from the algorithm’s meta.json visual.components field.

Layouts are not tied to individual algorithms. Multiple algorithms share the same layout. For example, binary search, bubble sort, and quicksort all use the array-with-pointers layout with different configurations.


Layouts are managed through a registry that maps layout names (strings) to layout implementations.

Registers a new layout implementation in the global registry.

function registerLayout(name: string, factory: LayoutFactory): void
ParameterTypeDescription
namestringThe layout identifier. Must match the visual.layout values used in meta.json.
factoryLayoutFactoryA factory function that creates a layout instance given configuration.

Retrieves a registered layout by name.

function getLayout(name: string): LayoutFactory | undefined
ParameterTypeDescription
namestringThe layout identifier to look up.

Returns undefined if no layout is registered with the given name.


Eigenvue ships with 10 built-in layouts, organized by domain.

Layout NameCategoryVisual PrimitivesAlgorithms
array-with-pointersClassicalArray cells, named pointers, value labels, index labelsBinary search, bubble sort, quicksort (partition view)
array-comparisonClassicalDual array tracks, merge indicators, comparison arcsMerge sort
graph-networkClassicalNodes, edges, labels, visited/unvisited statesBFS, DFS, Dijkstra
neuron-diagramDeep LearningNeurons, connections, activation values, bias nodesSingle neuron, perceptron, activation functions
layer-networkDeep LearningLayer columns, inter-layer connections, weight labels, gradient overlaysForward propagation, backpropagation, neural network training
convolution-gridDeep LearningInput grid, kernel overlay, output grid, product annotations2D convolution
loss-landscapeDeep Learning3D/contour surface, position marker, gradient arrows, trajectory pathGradient descent, SGD, momentum, Adam
token-sequenceGenerative AIToken cells, merge animations, embedding vectors, bar chartsBPE tokenization, token embeddings
attention-heatmapGenerative AIToken rows/columns, heatmap cells, Q/K/V matrices, score annotationsSelf-attention, multi-head attention
layer-diagramGenerative AISublayer blocks, data flow arrows, residual connections, norm indicatorsTransformer block

Every layout must implement the following contract.

The layout receives its configuration from the algorithm’s meta.json at initialization time:

interface LayoutConfig {
/** The visual.components object from meta.json. */
readonly components: Readonly<Record<string, unknown>>;
/** The visual.theme overrides from meta.json. */
readonly theme?: {
readonly primary?: string;
readonly secondary?: string;
};
/** Canvas dimensions (set by the canvas manager). */
readonly width: number;
readonly height: number;
}

The layout’s primary responsibility is to render a step’s visual state onto the canvas. This involves:

  1. Reading the step’s state to determine positions, values, and structural data.
  2. Processing the step’s visualActions in order, mapping each action type to changes in visual primitives.
  3. Drawing visual primitives onto the canvas using the rendering engine’s drawing API.

The layout maintains a mapping from action types to handler functions:

type ActionHandler = (action: VisualAction, state: Record<string, unknown>) => void;

For unknown action types, the layout must silently skip the action (no-op). This preserves the open vocabulary design — new action types can be added without breaking existing layouts.


Each algorithm’s meta.json specifies its layout and optional configuration in the visual section:

{
"visual": {
"layout": "array-with-pointers",
"theme": {
"primary": "#38bdf8",
"secondary": "#0284c7"
},
"components": {
"pointers": [
{ "id": "left", "label": "L", "color": "#38bdf8" },
{ "id": "right", "label": "R", "color": "#38bdf8" },
{ "id": "mid", "label": "M", "color": "#f472b6" }
],
"showIndices": true,
"showValues": true,
"highlightColor": "#fbbf24",
"foundColor": "#22c55e"
}
}
}

The components object is entirely layout-specific. The rendering engine passes it through to the layout without interpretation. Each layout defines its own expected configuration shape.


Component KeyTypeDescription
pointersarrayArray of pointer definitions with id, label, and color.
showIndicesbooleanWhether to display 0-based index labels below cells.
showValuesbooleanWhether to display values inside array cells.
highlightColorstringDefault highlight color (hex).
foundColorstringColor for the found element (hex).
dimColorstringColor for dimmed/eliminated elements.
compareColorstringColor for elements being compared.
Component KeyTypeDescription
tracksnumberNumber of array tracks to display (typically 2).
showMergeZonebooleanWhether to show the merge output zone.
Component KeyTypeDescription
directedbooleanWhether edges have direction (arrows).
weightedbooleanWhether to display edge weights.
nodeRadiusnumberRadius of graph nodes in pixels.
Component KeyTypeDescription
showBiasbooleanWhether to display bias inputs.
activationFnstringDefault activation function to display.
Component KeyTypeDescription
layerSizesnumber[]Number of neurons per layer for initial layout.
showWeightsbooleanWhether to display weight values on connections.
showGradientsbooleanWhether gradient display is enabled.
Component KeyTypeDescription
inputSizenumber[]Input grid dimensions [rows, cols].
kernelSizenumber[]Kernel dimensions [rows, cols].
showProductsbooleanWhether to show element-wise products.
Component KeyTypeDescription
surfaceTypestringSurface rendering mode: "contour" or "3d".
showTrajectorybooleanWhether to display the optimization trajectory.
showGradientbooleanWhether to show gradient arrows.
Component KeyTypeDescription
showIdsbooleanWhether to display token IDs.
showEmbeddingsbooleanWhether the embedding vector panel is visible.
Component KeyTypeDescription
showScoresbooleanWhether to show raw scores alongside weights.
showProjectionsbooleanWhether Q/K/V projection panels are visible.
colorScalestringColor scale for heatmap cells (e.g., "blues", "viridis").
Component KeyTypeDescription
sublayersstring[]Ordered list of sublayer IDs to display.
showResidualbooleanWhether to show residual connection arrows.
showNormbooleanWhether to show layer normalization indicators.

To add a new layout to the system:

  1. Create the layout module implementing the layout factory interface.
  2. Register it using registerLayout() during application initialization.
  3. Reference it in algorithm meta.json files via the visual.layout field.
import { registerLayout } from "@/engine/layout";
registerLayout("my-custom-layout", (config: LayoutConfig) => {
// Return a layout instance that implements the rendering contract
return {
render(step, canvas) {
// Read step.state and step.visualActions
// Draw primitives onto the canvas
},
dispose() {
// Clean up any resources
},
};
});

The layout name in registerLayout must exactly match the string used in meta.json’s visual.layout field.