Controls

The controls that set a scene’s parameters — moved by the reader, or by the scene through a cue control — where their stops come from, and how their values read.

#Declaring a parameter

A parameter is a named value a scene holds. A control declares one and gives the reader a way to move it: slider:a(range: [0, 4]) declares a, running from 0 to 4, and puts a row for it in the scene's footer. Every expression in the scene that mentions a re-evaluates as it moves.

The parabola y = ax² at a = 2, a point at (1, a) labelled a, and a steepness slider beneath the plot.The parabola y = ax² at a = 2, a point at (1, a) labelled a, and a steepness slider beneath the plot.
graph{
    slider:a(name: steepness; range: [0, 4]; step: 0.25; default: 2)
    curve(expression: a * x^2)
    point(x: 1; y: a; label: $a$)
}

The parameter is the scene's, not the environment's it is written in. A control may sit in any environment's body, in the scene's detail outside every environment, or inside a cue, which brings its row into the footer on that cue's step. Two graphs side by side read the same a, and one control declares it. A second control for the same name anywhere in the scene is refused:

graph{
    slider:a(range: [0, 4])
    curve(expression: a * x^2)
}
slider:a(range: [0, 2])

[canvas/parameter-declared-twice]'a' is declared twice in this scene — by 'slider' and by 'slider'. A parameter belongs to the scene, so one control declares it

Warning
An expression reads a parameter by its letter, so a parameter an expression uses is named with one letter. A word is read as a product of letters — rate is r·a·t·e — and is the name for a parameter no expression reads: one that picks a slice of data, below.

name: is what the row is labelled with in front of the reader, and is rich text, so name: Frequency $b$ sets its own maths. Without it the row is labelled with the parameter's letter.

#A letter no control declares

A letter in a graph or region expression that no control declares still gets a row: a slider from −5 to 5 in steps of 0.1, starting at 1. A control is how the author says what the letter ranges over, where it starts and what it is called.

#Where the stops come from

#Range, step and starting value

range: [start, end] sets the ends, step: the distance between stops and default: the starting value. Left out, they are [-10, 10], 1 and 0. An end may be a constant expression, range: [0, 2pi], and a default: outside the range starts at the nearer end.

The reader's track moves in steps of step:. A parameter's stops can fall off that ladder — the values of a column, below — and discrete holds it to the stops alone. On a cue.slider, discrete makes the sweep jump from stop to stop rather than glide through the gaps.

The footer draws a parameter as a track with its value beside it; under the numbers format the reader can also type the value. It draws a list instead when the values are names, or when the parameter is discrete and has between 2 and 12 stops.

#Stops written out

selector declares a parameter whose stops are written out with options:, or taken from a column with over: (below). It is always discrete, and takes no discrete:. A list of numbers gives a numeric parameter with whatever stops the list holds, in ascending order. Any entry that is not a number makes the whole list names, in the order written: [1, 2, many] is three names.

Five readings with a straight line fitted through them, and a fit degree menu beneath the plot set to 1.Five readings with a straight line fitted through them, and a fit degree menu beneath the plot set to 1.
graph{
    scatter#readings(x: 1, 2, 3, 4, 5; y: 2.0, 2.4, 3.1, 4.8, 7.4)
    selector:d(name: fit degree; options: [1, 2, 3, 4])
    fit(of: #readings; degree: d)
}

A parameter can stand anywhere an attribute takes an expression, and fit's degree: is one: the fit is recomputed at whichever degree the reader picks, and its derived slope, intercept and r2 follow it.

Warning
Where the stops are names, default: is an index among them, counted from 0. Writing the opening choice first in options: does the same thing.

#Stops from a column

over: takes the stops from a dataset column instead. A column of numbers gives its distinct values in ascending order as the stops, the smallest and largest as the range, and the closest two values' gap as the step; the parameter starts at the smallest. A column of text gives its distinct names, in the order the rows first give them.

Warning
Beside over:, range: is not read.

A mark with slice: on the same column draws only the rows at the parameter's value. The mark names the column and never the parameter: the two meet at the column.

Population in millions of Brazil and India for one year at a time, with a Year slider beneath the plot.Population in millions of Brazil and India for one year at a time, with a Year slider beneath the plot.
graph{
    @data#pop{
        country | year | millions
        Brazil  | 1960 | 72
        Brazil  | 1990 | 149
        Brazil  | 2020 | 213
        India   | 1960 | 451
        India   | 1990 | 873
        India   | 2020 | 1396
    }

    slider:t(over: #pop.year; name: Year)
    bar(x: #pop.country; y: #pop.millions; slice: #pop.year)
}

On a numeric column, a parameter standing between two values the data holds draws each mark at the readings interpolated between those two rows. discrete keeps it on the rows. On a column of names nothing lies between two stops, and the mark draws the rows carrying the chosen name:

@data#pop{
    country | year | millions
    Brazil  | 1960 | 72
    Brazil  | 2020 | 213
    India   | 1960 | 451
    India   | 2020 | 1396
}

graph{
    selector:country(over: #pop.country)
    bar(x: #pop.year; y: #pop.millions; slice: #pop.country)
}(
    x format: discrete
)

options: and over: are two sources for the same stops, and a control with both is refused:

selector:country(options: [Brazil, India]; over: #pop.country)

[canvas/control-stops]'selector' gives both 'options:' and 'over:', which are two claims about the same list of stops; keep one

#Reading the value

#How a value reads

format: sets how the value reads in the footer. Every format but names places the value on a number line, so range: and step: stay numbers and only the readout changes.

  • numbers — the number itself, to as many places as step: has.
  • dates — a year with its fraction, read as the day it falls on.
  • months — 1 to 12, read Jan to Dec.
  • weekdays — 1 to 7, read Mon to Sun.
  • hours — hours from midnight, read as a clock time: 14.5 is 14:30.
  • weeks — a year with its fraction, read as a year and week: 2020-W05.
  • names — the data's own words, with no number line.
Air temperature across one day as a grey curve, an orange point on it at 14:30, and a Time slider beneath the plot reading 14:30.Air temperature across one day as a grey curve, an orange point on it at 14:30, and a Time slider beneath the plot reading 14:30.
graph{
    slider:h(format: hours; range: [0, 24]; step: 0.25; default: 14.5; name: Time)
    curve(expression: 14 + 6 * sin(2 * pi * (x - 9) / 24); colour: grey)
    point(x: h; y: 14 + 6 * sin(2 * pi * (h - 9) / 24); colour: orange)
}(
    x domain: [0, 24]
    y domain: [5, 23]
)

auto, the default, takes the format from the column over: names, and is numbers without one. A format: written beside over: is used instead of the column's: a column of 1 to 12 reads as numbers unless it says months.

#Reading the value in prose

Every control derives value, the parameter's current value. It is reached through the control's node id, so a control whose value the prose cites is written with one:

scene{
    At a spread of {{#spread.value}}, the curve is this wide.
}[
    graph{
        slider#spread:s(range: [0.5, 3]; step: 0.5; default: 1)
        curve(expression: exp(-x^2 / s))
    }
]

A parameter of names publishes the name itself.

#Controls the scene moves

cue.slider and cue.selector are the two controls with the scene moving the parameter. Each takes its plain twin's attributes, plus at:, the anchor the movement starts on, and duration:. Before the anchor fires, the parameter stands at its default:.

  • A cue.slider runs from its default: to the end of its range. duration: is the time for the whole range, 8s when left out, so a default: part way along finishes in proportionally less. easing: is linear unless set to smooth.
  • A cue.selector stands on each choice in turn and holds the last. duration: is the time on each choice, 2s when left out, so five choices at 2s take ten seconds. It takes no easing:.
region:world{
    choropleth(regions: #rates.country; values: #rates.rate; slice: #rates.year)
    cue.slider:t(over: #rates.year; at: 1; duration: 12s)
}

The movement is part of the step's choreography, so the step's play, pause and replay drive it. The reader keeps the control:

  • Moving it while the step is still playing pauses the scene, and the parameter stays where they put it.
  • Pressing play returns it to where the scene had it when they took hold, and the choreography carries on from that moment.
  • Replaying the step runs every control they did not touch from its start. One they moved stays where they put it.
  • Leaving the step hands every control back to the scene.

#Writing guidance

  • Steps, timers, sweeps & parameters — New content on the step that introduces it, timers for timed motion, sweeps for a change the prose describes, and parameters where one sentence holds across the range.

Last updated