Skip to content

Scenes ​

Same chart, different states — composed into a story that a reader can step through.

Why this matters ​

Journalism rarely sits still on a single chart. A finding has a build-up, a turn, and a punchline. In Blueprint Chart, a scene is a named visualisation state — the same chart with different data, highlighting, annotations, or styling — and a sequence of scenes is the chart's story. You write scenes in the same .bpc document, and the runtime gives the reader Previous / Next controls (or your own UI) to walk through them.

Quickstart ​

A bar chart with four narrative beats — the first three re-spotlight a different country, and the last reframes the same story per capita:

bpc
chart bar-horizontal {
  theme = "blueprint-bold"
  colorPalette = "BlueprintBold"
  title = "China emits more CO₂ than the US and India combined"
  description = "Annual emissions in billion tonnes, 2023"
  source = "Our World in Data"
  sourceUrl = "https://ourworldindata.org/co2-emissions"
  byline = "Pierre Romera"

  data {
    "China" = 12.17
    "United States" = 4.92
    "India" = 3.06
    "Russia" = 1.73
    "Japan" = 0.99
    "Germany" = 0.59
  }

  scene "China spotlight" {
    title = "China alone emits more than the US and India together"
    highlight "China"
  }

  scene "India rising" {
    title = "India passed the EU to become the third-largest emitter"
    highlight "India"
  }

  scene "Japan declining" {
    title = "Japan's emissions have fallen since their 2013 peak"
    highlight "Japan"
  }

  scene "Per capita reframe" {
    title = "Per person, Gulf states dwarf the biggest emitters"
    description = "CO₂ emissions per capita, tonnes per person, 2023"
    highlight "Qatar"
    data {
      "Qatar" = 40.13
      "United Arab Emirates" = 19.77
      "United States" = 14.32
      "China" = 8.56
      "India" = 2.13
    }
  }
}

From the sample library

This is packages/lib/src/samples/co2-emissions-story.bpc verbatim. The first three scenes keep the same base data and only shift the title and highlight; the fourth swaps in per-capita data for a closing reframe.

Open this in the editor to see the scene timeline appear automatically; embed it on a page and readers get a Previous / Next nav.

How it works ​

A scene block accepts the same member set as the top-level chart. At parse time, each scene becomes a SceneNode in the AST. The DSL converter merges scene members on top of the base chart to produce the effective ChartData and ChartOptions for that scene.

At render time:

  1. The runtime collects the chart's scenes into a SceneDefinition[].
  2. createSceneController(container, scenes, onSceneChange) injects a small <nav> (Previous / Next / counter) into the container.
  3. On every scene change, the callback re-renders the chart with transition = true, which triggers the motion helpers (snapshotForFadeOut, commitFadeOut, fadeIn) and animates the crossfade.
  4. goTo(index) clamps and wraps so goTo(-1) cycles to the last scene.

The pipeline that runs per scene is the same eleven-step sequence documented in Embedding and the DSL spec — scene overrides are merged at step 3 (transforms), so everything downstream sees the post-scene state.

Recipes ​

Highlight a different point per scene ​

The short-form highlight "<name>" is the workhorse. Drop one per scene and the rest of the chart greys out:

bpc
scene "China spotlight" {
  theme = "blueprint-bold"
  colorPalette = "BlueprintBold"
  title = "China alone emits more than the US and India together"

  highlight "China"
}

scene "India rising" {
  title = "India passed the EU to become the third-largest emitter"

  highlight "India"
}

From the sample library

Two adjacent scenes from packages/lib/src/samples/co2-emissions-story.bpc. Each scene retitles the chart and shifts the spotlight without touching the base data.

Replace data wholesale in a scene ​

Any scene can carry its own data block, which replaces the base data for that scene's render. The Bulgaria scene from farm-compass drops the EU-wide aggregate in favour of a country-specific time series, and keeps the same area-stacked chart type:

bpc
scene "Bulgaria: subsidies explode" {
  theme = "blueprint-bold"
  colorPalette = "BlueprintBold"
  title = "Bulgarian farmers received almost nothing before 2007 accession"
  description = "More than three-quarters of Bulgarian subsidies are direct payments — the highest share among new members"

  data {
    series = "Indirect subsidies","Direct subsidies"
    "2000" = 0,5
    "2004" = 0,67
    "2007" = 59,250
    "2010" = 79,466
    "2013" = 132,852
    "2015" = 213,677
  }

  highlight "Direct subsidies"
}

From the sample library

Trimmed scene from packages/lib/src/samples/farm-compass.bpc (full sample has the 2000–2015 yearly series). The scene swaps data and keeps the parent chart's area-stacked type.

Switch chart type mid-story ​

A scene can override the chart type with type =. The same farm-compass story leaves the parent area-stacked chart and pivots to a line for the "farms grew" beat, then back to an area-stacked later on:

bpc
scene "Bulgarian farms grew" {
  theme = "blueprint-bold"
  colorPalette = "BlueprintBold"
  title = "Average farm size in Bulgaria quadrupled"
  description = "Average farm size in hectares"
  type = line

  data {
    "2005" = 5
    "2007" = 6
    "2010" = 12
    "2013" = 18
  }
}

From the sample library

Scene #4 of packages/lib/src/samples/farm-compass.bpc. The story changes chart type four times across its nine scenes — area-stacked → line → area → area-stacked → line-multi — all from one document.

Control how long an annotation stays ​

By default an annotation shows only in the scene where it is declared. Use repeat to carry it into later scenes:

bpc
scene "Crash" {
  theme = "blueprint-bold"
  colorPalette = "BlueprintBold"
  range {
    start = 2008
    end = 2009
    text = "Financial crisis"
    repeat = 1         // this scene plus the next one (2 scenes total)
  }
}

scene "Recovery" {
  annotation "2015" {
    text = "Paris Agreement"
    repeat = true      // this scene and every scene after it
  }
}

repeat accepts three forms:

  • false or omitted (the default): show only in this scene.
  • a positive integer N: show in this scene plus the next N scenes, so repeat = 1 spans 2 scenes and repeat = 3 spans 4.
  • true: show in this scene and every scene after it.

A top-level annotation (declared on the chart itself, outside any scene) anchors at the base chart frame, the first thing a reader sees. With no repeat it appears only there; repeat carries it forward into the scenes that follow.

Drive playback from your own UI ​

The controller is decoupled from the nav DOM it inserts — you can ignore the built-in buttons and call next(), previous(), or goTo(index) from any custom UI:

ts
import { createSceneController } from '@blueprint-chart/lib/runtime'

const controller = createSceneController(container, scenes, (scene, index) => {
  renderChart(canvas, scene.data, true)
})

document.querySelector('#my-next-button')!.addEventListener('click', () => {
  controller.next()
})

// Programmatic jump:
controller.goTo(2)

Call controller.destroy() to remove the injected nav when tearing the chart down.

API surface ​

Exported from @blueprint-chart/lib/runtime:

SymbolOne-liner
createSceneController(container, scenes, onSceneChange)Build a scene controller, inject Previous / Next nav, call back on every scene change.
SceneDefinition (type){ name: string, data?: Record<string, unknown> } — one scene's payload.
SceneController (type)Returned object with currentScene, totalScenes, next(), previous(), goTo(index), destroy().
createStepController / StepDefinition / StepControllerDeprecated aliases retained for backward compatibility — new code should use the Scene* names.

Motion helpers used internally during scene transitions (all exported from @blueprint-chart/lib):

SymbolOne-liner
getTransitionDuration()Canonical fade duration shared with the editor's UI transitions.
snapshotForFadeOut(container)Capture the outgoing DOM for fade-out.
commitFadeOut(snapshot)Animate the snapshot out.
fadeIn(container)Animate the new render in.
getCachedChart(container)Last render, for diff-based interpolation.

DSL converter helpers (also exported from @blueprint-chart/lib):

SymbolOne-liner
extractSceneOverrides(ast)Pull each scene's merged ChartData / ChartOptions from a parsed AST.
SceneNode (type)AST node for a scene block.

See the full list in the API reference.

See also ​

Released under the MIT License. Built static-first — your data never leaves the page.