IsoclineInteractive economics Gallery Manual Builder Code editor

Spec reference (v0.1)

A complete field-by-field reference for the graph spec, with the correct term for each thing.

How to talk about it

The file is a spec, written as a JSON object. An object is a set of properties. Each property has a name (the key, before the colon) and a value (after it). A value can be a number, a string, a boolean, an array (a list, in square brackets), or another object (nested properties, in curly braces).

So when you see:

"axes": { "x": { "label": "Quantity", "min": 0, "max": 60 } }

you would say: axes is a top-level property; its value is an object with an x property; the value of x is an axis object; and label, min, max are properties of that axis object. To point at one precisely, say "the min of the x-axis."

Two more terms you will use constantly. A value written as a string like "demandIntercept + slope*x" is an expression, evaluated live against the scope (all your params and derived values). And each entry in the objects list is a graph object (or primitive), whose type property picks which kind it is.

Throughout this reference, expr means "a number, or an expression string." point means a two-element array [expr, expr].

Top-level properties

Property Value Required Meaning
version string yes Spec version, currently "0.1".
title string no Drawn as a heading above graphs, games, and trees. Renders $...$ math.
titleSize number no Font size of the title in graph units (default 20). Set in Computer Modern bold, starting at the y-axis; it scales with the graph like the graph's own text, and is still scaled by fontScale.
fontScale number no Scales all text in the graph at once (1 = default, 1.3 = 30% larger). Good for slides/projection.
graphWidth number no Graph drawing width in px (default 640). Smaller values help the sidebar fit beside the graph.
sidebarWidth number no Sidebar width in px (default 290).
sidebarPosition string no "right" forces the sidebar beside the graph even when space is tight; "bottom" forces it below; omit for automatic (beside when wide, below when narrow).
params array of param no The adjustable variables (become sliders / drag targets).
derived object of name to expr no Values computed from params. See Scope below.
colors object of name to color no Add or override palette color names.
axes axes object one of the five Single-panel graph. Pair with objects.
objects array of graph object with axes The things drawn in the single panel.
panels array of panel one of the five Several stacked graphs instead of axes+objects.
game game object one of the five A normal-form (matrix) game.
tree tree object one of the five An extensive-form game tree.
repeated repeated object one of the five An infinitely repeated 2×2 game (matrix + discounting analysis).
sidebar sidebar object no Sliders and text beside the graph.

You provide exactly one of these five content forms: axes+objects (single panel), panels (multiple panels), game (a matrix game), tree (a game tree), or repeated (a repeated game). params, derived, colors, and sidebar may accompany any of them.

Sizing and layout

Two levels of text sizing, and they compose:

Layout is controlled by graphWidth, sidebarWidth, and sidebarPosition (above). By default the graph and sidebar sit in a row that wraps the sidebar underneath when the container is too narrow (roughly under ~700px for a 640px graph plus a 290px sidebar). sidebarPosition: "right" stops the wrap and shrinks the graph instead; "bottom" always stacks the sidebar below. The plot's bottom/left margins grow automatically with the axis font size so large axis labels never clip.

In the visual builder, all of these live in the meta section (font scale, title size, graph width, sidebar width, sidebar position) and on each axis / label / sidebar item; every number box is pre-seeded with its default value, and a value is only written into the spec once you change it away from the default.

The scope: params and derived

param (an entry in params):

Property Type Required Meaning
name string yes How you refer to it in expressions.
value number yes Starting value.
label string no Caption shown on its slider.
min, max number no Range. Having both makes a slider and bounds dragging.
step number no Rounding increment (e.g. 0.1).

derived is an object mapping a name to an expression. Entries are computed in order and may use earlier ones. A value may also be a nested object of expressions (used to group related values):

"derived": {
  "qStar": "(supplyIntercept - demandIntercept) / (demandSlope - supplySlope)",
  "pStar": "demandIntercept + demandSlope * qStar"
}

Params and derived together form the scope: the set of names any expression in the spec can use.

Axes and panels

axis object: label (string), min (number, required), max (number, required), ticks (number; 0 for none, omit for ~6), color (optional palette name or hex; tints the axis line, tick labels, and axis title — defaults to dark ink), size (optional px; sizes this axis's title and its tick numbers, default 16 for the title and 14 for tick numbers, still scaled by fontScale). Set color/size in the builder via the axis form.

Each axis is drawn a little past its last tick number, by 0.2 of a tick step: an axis from 0 to 10 in steps of 2 ends at 10.4. Tick numbers stay on the authored min–max range, without tick marks. The x-axis title sits below the tick numbers, right-flushed at 0.25 of a step past max (10.5 in that example); the y-axis title is rotated, left of the tick numbers, with its end flush at the same point on the y-axis. Tick numbers and axis titles never move: a label (a curve label, a dropline label, any other) that lands on them is moved below the x tick numbers or left of the y tick numbers, or, when that space is taken or off the graph, just above the x-axis or right of the y-axis.

axes object: { "x": axis, "y": axis }.

panel (an entry in panels): axes (required), objects (required, array of graph objects), id (string, optional), height (number, optional plotting height in pixels; defaults to 320 for a single panel and 210 for each panel in a multi-panel graph).

Graph objects

Every graph object has these three in common: type (string, required, selects the kind), color (a palette name or hex), and show (a boolean expression; when it is false the object is hidden). Curves, lines and segments also take dashArray (an SVG dash pattern string, e.g. "2 6" for dots or "8 5" for dashes) and lineCap ("butt", "round" or "square"; use "round" with a short dash for round dots). Below, only the type-specific properties are listed. In the visual builder every color / fill / stroke field is a color picker (a wheel for any color, plus a clear-to-default button).

curve — a function y = f(x)

Property Type Required Meaning
fn expression in x yes The function to plot.
min, max expr no x-range to draw over (defaults to the axis range).
strokeWidth number no Line thickness.
opacity expr no 0 to 1 (default 1), clamped. Re-evaluated on every update, so a curve can fade in or out with a param. Its label fades with it.
label label no Text on the curve.

line — a straight line

Give it either two points, or a point and a slope.

Property Type Required Meaning
point point one form A point it passes through.
point2 point with point A second point (draws the segment between the two).
slope expr with point Slope, instead of a second point.
intercept expr alt. y-value at x = 0 (used with slope, no point).
min, max expr no x-range for the slope form.
strokeWidth number no Thickness.
label label no Text on the line.

point — a marked coordinate

Property Type Required Meaning
x, y expr yes Its location.
r number no Radius in pixels.
droplines object no { "x": ..., "y": ... }. A value of true drops a dashed line to that axis; a string drops the line and prints that (dynamic) text as a KaTeX label.
draggable drag no Lets the user drag it to change a param.
label label no Text by the point.
hint string no Small caption (e.g. "drag").

drag object: param (string, required, which param to change), axis ("x" or "y", which direction), expression (optional; maps the dragged coordinate, available as drag, to the param value, e.g. "drag / xIntercept").

area — a shaded region between two curves

Property Type Required Meaning
top expr in x yes Upper edge. A number (e.g. 0) or an expression string both work.
bottom expr in x yes Lower edge. A number or expression string.
xFrom, xTo expr yes The x-range to fill.
opacity number no 0 to 1 (default 0.15).

rectangle

Property Type Required Meaning
a, b point yes Two opposite corners.
fill color or "none" no Interior.
stroke color no Border.
opacity number no 0 to 1.

arrow

Property Type Required Meaning
begin, end point yes Endpoints.
double boolean no Arrowheads on both ends.
trim number no Fraction to shorten each end (e.g. 0.01).
label label no Text at the middle.

segment — a plain line between two points

a (point), b (point), strokeWidth (number), label.

label — free-floating text

text (string; may contain LaTeX, $...$ delimiters, and ${expr} interpolation), x (expr), y (expr), plus the label properties below (size, color, dx, dy, align, position, positionRules).

Labels

Wherever a label property appears it is a label object:

Property Type Meaning
text string The text. Rendered with KaTeX. A string with no $ is rendered whole as math, so bare LaTeX like Q^d or \text{price} works. It also accepts $...$ / $$...$$ delimiters like the sidebar, so $x$ and mixed text like price is $p$ render correctly. May contain ${expr}, replaced by the live value.
x expr Where along the object to place it.
dx, dy number Pixel nudges. With a compass position these are extra nudges (default 0); without one the existing defaults remain (dx: 6, dy: 0).
align string "right" right-aligns it when no compass position is selected.
position string Direction from the point to the label: N, NE, E, SE, S, SW, W, NW, or C (center). Edges/corners sit 6 pixels away; the whole label stays on the chosen side.
positionRules array Ordered { "when": expression, "position": direction } rules, evaluated against live parameters and derived values on every update. First true rule wins; otherwise use position, or the existing offsets if omitted.
color color Text color.
inline boolean Curves and lines only. Sets the label on the line itself, centred at x and rotated to the line's angle, in the line's colour (unless color is set), on a backing of the colour behind the graph so the line appears to pass under it. position and positionRules are ignored; dx/dy still nudge it.
background boolean or color A backing behind the text so lines and droplines do not run through it. true uses the paper colour; a palette name or hex sets it explicitly.
size number Font size (px) of this label (default 16). Still scaled by fontScale.

For example, keep AC(q) northeast through q = 3.50, then southeast:

{
  "text": "$AC(q)$",
  "position": "NE",
  "positionRules": [{ "when": "q > 3.5", "position": "SE" }]
}

To use more intervals, add ordered rules such as q <= 3.5 → NE, q <= 7 → SE, with position: "SW" as the fallback. Conditions may combine parameters and derived values (q > 3.5 and p < ACq). A malformed condition is treated as a non-match. Rules respond to sliders, dragging and reset in the same update as the graph. There is no automatic collision detection.

Compass positions work for labels on points, curves, lines, segments and arrows, and on free-standing type: "label" objects. They replace the old automatic vertical lift on curves/lines/arrows; specs without compass positions keep their existing placement. Annotation box anchors remain a separate API. In the visual builder, choose a default position and add ordered position rules in the label form.

Sidebar

sidebar object: { "controls": [ control, ... ], "hidden": false }.

control: title (string), titleSize (number, px, default 15), sliders (array), divs (array).

Games (normal form)

Set the top-level game property (instead of axes/panels) to draw a payoff matrix with best-response and Nash reasoning built in. The runtime picks the view: a plain matrix, an IESDS step-through, or the 2×2 mixed-strategy diagrams, depending on the flags below.

game object:

Property Type Required Meaning
players array of exactly two game player yes Row player first, column player second.
payoffs 3-D array yes payoffs[i][j] is a two-element array [u1, u2] — the row player's and column player's payoff when the row player picks strategy i and the column player picks strategy j. Each entry is an expr, so payoffs can depend on params.
showBestResponses boolean no Tint each player's best-response cells in that player's color (the visible, non-bold cue).
showNash boolean no Ring the pure-strategy Nash equilibria (cells that are a best response for both).
iesds boolean no Iterated elimination of strictly dominated strategies: adds Prev/Next buttons that dim eliminated strategies one round at a time and outline the dominating strategy.
mixed boolean no 2×2 only. Adds the mixed-strategy view: best-response-in-the-unit-square diagram, indifference plots, and p/q sliders.
title string no Heading for the game.

game player: name (string, required), strategies (array of strings, required — the row or column labels), color (optional palette name or hex; defaults to the built-in player1 / player2 colors). The builder's game mode has a color picker for each player.

Grid convention: payoffs.length = number of row strategies = players[0].strategies.length; each payoffs[i].length = number of column strategies = players[1].strategies.length. For mixed and iesds's cleanest behavior, keep it 2×2; the plain matrix, best-response tint, and Nash ring work for any m×n.

Trees (extensive form)

Set the top-level tree property to draw an extensive-form game, laid out automatically (no manual coordinates) and optionally solved by backward induction.

tree object:

Property Type Required Meaning
players array of tree player yes Index order matters: a node's player and a terminal's payoffs are positional to this list.
root tree node yes The top of the tree.
orientation string no "topDown" (default) or "leftRight".
backwardInduction boolean no Adds Prev/Next fold-back: reveals decisions from the leaves inward, bolds each chosen edge in the acting player's color, shows the value folding back, and rings the subgame-perfect outcome.
title string no Heading for the tree.

tree player: name (string, required), color (optional; defaults to the player1..player3 / nature palette by index).

tree node is recursive and is one of two kinds:

tree action: label (string, the edge label / action name) and child (a tree node — the node reached by taking that action).

Depth and breadth: 2+ players and arbitrary branching are supported; layout is automatic so wide or deep trees just get more spread. Very large trees will eventually crowd, but there is no fixed cap for teaching-sized games.

Repeated games

Set the top-level repeated property to draw an infinitely repeated 2×2 game (a prisoner's dilemma by default): the stage matrix, a discounting timeline (cooperate-forever vs the best one-shot deviation), and a threshold plot where the two discounted values cross at δ*. A strategy selector switches between grim trigger, tit-for-tat, and an n-period-forgiving trigger.

repeated object: all optional. players (two names, default You / Rival), cooperate / defect (action labels), T / R / P / S / delta / n (the scope names the runtime reads the stage payoffs, discount factor, and punishment length from — defaults are those literals, so just define matching params), strategies (subset of ["grim", "tft", "nPeriod"], default all three), periods (timeline horizon, default 9). The payoffs and δ live in params so they become sliders, and every threshold updates live. Grim trigger sustains cooperation iff δ ≥ (T−R)/(T−P); tit-for-tat needs a higher δ; the n-period trigger sits between the two.

Dynamics (discrete-time paths)

There is no dedicated "dynamics" primitive. Discrete-time models (like the cobweb) are authored with two existing tools:

  1. Unroll the recurrence into derived. Because derived entries compute in order and can reference earlier ones, "p1": "45 - 1.5*p0", "p2": "45 - 1.5*p1", … is the state path. Define one entry per period up to a fixed maximum.
  2. Gate each step with a periods slider. Make an integer param periods (min:0 … max:n, step:1) and put "show": "periods>=k" on every segment and point belonging to step k. Dragging periods then reveals the path one period at a time — ideal for lecturing.

Reactive verdict text (which stability regime you are in) is just sidebar divs with show expressions on a stability quantity, e.g. "show": "abs(K) > 1". The two cobweb samples (samples/cobweb.spec.json, samples/partial-cobweb.spec.json) are the worked examples, and python/kgspec/dynamics.py's cobweb(...) generates the pattern programmatically. Caveat: the recurrence is unrolled at authoring time, so the maximum horizon is fixed by how many derived entries you write; there is no runtime loop.

Expressions

Any field marked expr is a number or a string evaluated by mathjs against the scope. Inside a curve/area, the variable x is also available; inside a drag expression, the dragged coordinate is drag. You can use + - * /, ^ for powers, comparisons, and / or, a ternary cond ? a : b, member access like demand.slope, and math functions like sqrt(...), min(...), abs(...). An object whose expression throws (e.g. a misspelled name) is isolated — it simply does not draw, with a one-time console warning, rather than blanking the whole graph.

Colors

A color is a palette name or a hex/rgb string. Built-in names include the plain set (blue, red, green, orange, purple, pink, gray, black), the managerial aliases (demand, supply, price, equilibrium, surplus, profit, marginalRevenue, cost, budget, utility, arrow, fill3), and the game/tree player colors (player1, player2, player3, nature, plus cooperate / defect for repeated games). Add or override any of these with the top-level colors property, for example "colors": { "demand": "#1d4ed8" }. In the visual builder, any color field (object colors, fills, label text, axis color, game player colors) is a color picker: a wheel for any color plus a clear-to-default button. Picking from the wheel writes a hex; a named palette color already in a spec is preserved and shown resolved to its hex, and names can still be typed by hand in the code editor.