# Chalk manual The reference for Chalk, the language internotes are written in: every construct, the types its attributes accept, and the constraints the language assumes but cannot check. Every page of the manual follows, in reading order. --- # Grammar The whole shape of a construct: sigils, the head, the three brackets, and when a line is prose. ## Prose and constructs A Chalk document is plain text, and everything in it is a *construct* — a node, a micronode, or a directive. A bare line of writing is already one: it compiles to a `paragraph` node. A **named node is recognised by the bracket after its name**, so a line beginning `Note: this is important` compiles as a paragraph even though `note` is a node. Micronodes and directives carry their own sigil, so they need no bracket: `!cite:#fs1969()` and `@include:overview.part.chalk` stand bare. A few constructs have *marker spellings* instead of a written name: `# ` opens a heading node, `- ` a list, `---` a divider and `===` a step, and `**…**`, `*…*`, `` `…` `` and `$…$` are micronodes with their own delimiters. A marker is an alternate spelling, not a fourth kind — what it opens is an ordinary node or micronode. ```chalk scene{ # Falling objects Galileo's insight was that mass does not matter. }[ canvas.graph#drop{ curve#h(expression: 100 - 4.9x^2) }( x-axis-label: t y-axis-label: height ) ] ``` That is a whole valid document: one `scene` whose body holds the narrative and whose detail declares a graph environment with a single curve. ## The head Before its brackets, every construct is written the same way: ```chalk name[#id][:value] ``` `#id` assigns an identifier — available on every node, micronode and directive. `:value` supplies the construct's *implicit attribute*, which is whichever attribute the element declares first. Both are optional and independent, and the order is fixed: `#` before `:`. ```chalk curve#flight #flight names this curve image:photo.jpg :photo.jpg fills source, the first attribute image declares ``` An id and one attribute is all the head carries. **Everything else is an ordinary attribute written in the group**, and the two forms mean the same thing: ```chalk image#hero:photo.jpg(caption: Demand curve) image#hero(source: photo.jpg; caption: Demand curve) ``` The `:` value is a simple token: it starts after the colon and ends at the first bracket, whitespace, or end of line. Anything more complex — spaces, brackets, a list — is written as a named attribute instead. Because the head is read in that fixed order, a `#` *inside* the value is an ordinary character, which is what lets `!cite:#fs1969()` parse as a reference. ## The three brackets - `{ }` — the **body** - `[ ]` — the **detail**, which for a scene is its canvas - `( )` — the **attribute group** What is legal inside the body and the detail is the element's containment policy. Every reference page prints its policy under "Allowed content", and the reverse ("Allowed in") says where the element may be written. `Body & detail` covers the policies in full. > An empty group `()` is an attribute group with nothing in it. Since a bare node head compiles as paragraph text until a bracket follows, `image:photo.jpg()` is how a node with no other attributes is written. Micronodes need no such trick — their sigil already marks them. ## Attributes Attributes live in a parenthesised group, separated by semicolons: `point(x: 1; y: 2; colour: orange)`, or one per line when the group is written across several. The group comes last, after the body and detail. The control-flow directives are the exception — `@if(audience: teacher){…}` and `@for(n: 0..4){…}` put theirs first. Every value in Chalk is text until the attribute it lands in parses it, and a value that attribute's type cannot parse is a compile error — nothing is coerced or quietly defaulted. This is why a dataset column needs no type of its own: a cell reaches an attribute as text and is parsed there, failing exactly as the same text typed by hand would. A type may be narrower than its name suggests — a number restricted to whole values, or to a range, or a list held to a fixed length — and when a value is refused the error names the restriction it broke rather than just the type. The `type reference` lists what each one accepts. ## Sigils - `.` — namespace and member access: `canvas.graph`, `#islands.area` - `#` — assigns or refers to an identifier: `curve#flight`, `path: #flight` - `:` — supplies the implicit attribute: `image:photo.jpg` - `!` — a micronode: `!teal{…}`, `!upper{…}` - `@` — a directive: `@let(…)`, `@data#islands` - `*` — the `document header`, at the top of the file - `{{ }}` — an expression: `{{z-expected}}`, `{{2 * #f.slope}}` --- # Identifiers & references How `#id` names a construct, and the two ways to reach what it names. ## #id in the head Any construct may carry an identifier, written with `#` in its head before the implicit value: `curve#flight`, `canvas.graph#projectile`, `@data#islands`, `@preset#accent`. Every construct accepts one, including directives with no use for it. ### One namespace, document-wide Every `#id` in a document shares a single namespace, and identifiers resolve in both directions: a reference may point at something declared later. Two constructs claiming the same id is a collision, reported as an error — `@preset#apple` and `curve#apple` cannot coexist. ## Bindings A binding is a reference written bare — `#flight`, or with a member, `#islands.area`. It is never resolved to a value at compile time: the pointer survives into the renderer, and whatever consumes it decides what to do with it. ```chalk continue: #earlier-environment cue.trace{ point(x: 0; y: 0) }(path: #flight) table(data: #islands; columns: island, area) fit(of: #points; model: linear) ``` Only where the attribute's declared type is `reference`, though. Everywhere else those characters are just text: `label:` is rich text, so `text(label: slope = #f.slope)` renders the literal words rather than a number. Each reference page names the type of every attribute it takes. Because nothing is resolved early, a binding is *live*: it reflects animated attributes, reader-set parameters and derived values as they change. ## Substitutions `{{ }}` is the other way to reach a value, and a different mechanism: an `expression` the compiler evaluates, which is why it works identically in prose, attribute values and code bodies. A group holding one bare name reads as that name's value — a constant, a reference, an attribute — which is what makes it read like a substitution even though the mechanism underneath is arithmetic that happens to have nothing to do. ```chalk {{z-expected}} an @let constant {{#survey.area}} a dataset column, as a comma-joined list {{#flight.colour}} a plain, unanimated attribute {{body}}, {{detail}} reserved — see below ``` `{{body}}` and `{{detail}}` are reserved across the whole constant namespace. Inside an `@define` template they splice the content of the use's own `{...}` and `[...]` sections, so neither name may be declared as a template parameter or an `@let` constant. ```chalk @define:finding{ note{ {{body}} } }( body: required ) ``` Rejected by the compiler: `[attribute]` @define `finding`: `body` on node `finding` is a content policy — write `!none()`, `!all()`, `!literal()`, `!children(…)` or `!inline(…)` A section is declared as a *policy*, never as an ordinary parameter — and what it is declared as decides where it may be used. Written on a line of its own, `{{body}}` splices constructs and needs no declaration at all; used inside a line of text it needs one that makes it a value, `body: !inline(content: all)`. So the template above needs no attribute group, and the use supplies the body by writing one — `finding{Richness scales with area.}`. A **micronode head followed directly by a substitution takes it as the whole body**, so `!bold{{name}}` is `!bold{ {{name}} }` — without the spaces, which the spaced form leaks into the text it emits. At a run of three or more braces the innermost two open the substitution and the earlier ones are literal, which is how a body containing a reference is written: ```chalk !bold{{name}} the constant, as the body !bold{ {{name}} } the same, with a space either side of it !m{{{k}}} a body of "{" + the constant + "}" !bold{{a} b} an ordinary body — the interior is not a key ``` Micronodes only. A block head reads `{{…}}` groups inside it, so `quote#q{{cap}}` is genuinely ambiguous and compiles as paragraph text. A group abutting an implicit value is never part of that value either — an implicit value is a simple token — so `!format:comma{{price}}(places: 2)` reads the group as the body. > `{{#fit.slope}}` is no longer a compile error — reading a value that varies at render time makes the expression **bound** rather than refused: the compiler cannot finish it, so the tree travels to the renderer and is evaluated there, every frame. The same is true of an animated or reader-manipulated attribute. `Derived attributes` covers what a bound expression means for a construct that publishes one; a handful of positions the *compiler itself* has to read still refuse a bound value outright — an `@if` pair, an id, a `@data` cell — covered on `Expressions`. Every name still has to resolve, though. `{{2k}}` with no `k` declared is *undefined constant `k`*, reported at the line that wrote it, so a mistyped constant name is caught rather than quietly compiling. The one exception is an `equation`-typed position, where an unresolved name is a free name for the renderer to supply — see `Expressions`. ## Member access The `.` sigil reaches into whatever the identifier names: a dataset's column (`#islands.area`), a construct's attribute (`#flight.colour`), or a derived value (`#fit.slope`). It is the same operation as the namespace access in `canvas.graph`; the language draws no distinction between the two. - Column references must be fully qualified. There is no bare-column shorthand, because attribute values are unquoted and `label: island` must stay the literal string. - Existence is proven at compile time even though values are not — a reference to a construct or member that does not exist is an error, so the renderer never meets a dangling pointer. --- # Body & detail The two slots every construct may carry, and the policies that say what is legal in each. ## The { } body and the [ ] detail A construct may carry two bracketed slots after its head: a `{ }` **body** and a `[ ]` **detail**. Most elements use only the body. The `scene` is the one place the distinction is structural: its body is the narrative, its detail is the canvas. ```chalk scene{ The narrative — prose, headings, callouts. }[ canvas.graph{ curve(expression: x^2) } ] ``` ## Containment policies What is legal inside each slot is declared per element, and every reference page prints it under **Allowed content**. A policy is one of four things: a list of element ids, a prose description of what the slot accepts, a declared *type*, or nothing at all when the element takes no such slot. ```chalk canvas.graph{ A sentence about the curve. curve(expression: x^2) } ``` Rejected by the compiler: `[content-policy]` node `paragraph` is not allowed in `canvas.graph` when `plane: xy` - **A list of ids** — only those elements compile there. A `canvas.graph` body accepts marks and cues, nothing else. - **Prose** — the slot takes content rather than elements, like a paragraph's rich text or a `lines` chunk's literal code. - **A type** — a body may be declared as a value rather than as content, and is then parsed exactly as an attribute value is, so a wrong shape is a compile error rather than something odd on the page. Micronodes have taken one since D33, and a block node's body may too since D46 — this library types its values through attributes instead, so nothing here uses either. - **Nothing** — marks take no body; a mark's text is its `label:` attribute. ## Rich text and containment policy Where a page says a slot takes inline Chalk, the contents are prose with micronodes — `**bold**`, `$maths$`, `!teal{…}` — not nodes. The same is true of attributes typed `rich-text`: a mark's `label:` renders formatting and maths, but cannot hold a paragraph. Which means micronodes are not narrative-only. Every mark's `label:` is `rich-text`, as are a `parameter`'s `name:`, a graph's `x-axis-label` / `y-axis-label` and an environment's `title:` — so the same formatting, maths, colour and live `{{…}}` values that work in a paragraph work on the canvas. Each reference page names the type of every attribute, so `rich-text` in the type column is the authority on where. Every heading an element sets over its own content is the same type: a `table`'s `title:`, an `image`'s `caption:`, a `definition`'s `term:` and the `title:` of a `note`, `example` or `task`. A table of measurements is captioned in the notation its cells are written in, and a term as much notation as word — `$\epsilon$-$\delta$ continuity` — sets as one. ```chalk canvas.graph{ curve#bell(expression: exp(-x^2 / 2); label: $N(\mu, \sigma^2)$) point(x: 1; y: 2; label: !teal{turning point}) scatter#readings(x: 1, 2, 3; y: 2.0, 2.4, 3.1) fit#f(of: #readings; model: polynomial; degree: 1) text(x: 0; y: 4; label: slope = {{round(#f.slope, 2)}}) } ``` What a label cannot do is hold block content. Where a canvas needs that, the scene's detail takes it directly: `paragraph`, the four headings, `list` and `equation` are legal in both slots, and `table` and `image` in the detail only. There is one content vocabulary rather than a narrative set and a canvas set; the two slots differ by a short list of narrative-only elements — the four callouts, `divider`, `step` and the `internote` card. > A reference written *bare* in an ordinary body compiles as literal text: it is the *declaration* that makes a slot hold a reference, never the writing. A live value is reached with an expression instead — `{{#f.slope}}` — which works the same in a paragraph and in any `rich-text` attribute. ## Allowed in Every node's page carries an **Allowed in** section — the reverse of the policy, computed across the whole reference: which elements name this one in their body or detail. A policy list gives an element's permitted children; this gives its permitted parents. --- # Attributes The parenthesised group every construct may carry: how values are written, and the types that parse them. ## The attribute group Attributes live in a parenthesised group, written after the body and detail. On one line they separate with semicolons; across several, one per line with no separator. The two forms are equivalent, and a group may be empty — `()` — which is how a bare node head stops being prose. Which form to use where is a reading convention rather than a rule of the grammar; `Source style` states the one this manual follows. ```chalk point(x: 1; y: 2; colour: orange) canvas.graph{ curve(expression: x^2) }( x-axis-label: t y-axis-label: height ) ``` The control-flow directives are the one exception to the group coming last — `@if(audience: teacher){…}` and `@for(n: 0..4){…}` put theirs first. A semicolon separates attributes in *both* forms — one attribute per line included — so a value that needs one escapes it as `\;`: `table(title: Yield at 300K\; 1 atm)` is one title, and without the backslash it is an attribute called `1 atm`. Every other backslash passes through untouched, which is what leaves `$\pi$` intact in a `rich-text` value. ## Values as text, parsed by type Every value is text until the attribute it lands in parses it, and a value the attribute's type cannot parse is a compile error — nothing is coerced or quietly defaulted. A type may be narrower than its name suggests — a number restricted to whole values, or to a range, or a list held to a fixed length — and a refused value's error names the restriction it broke rather than just the type. The `type reference` lists what each type accepts. This is also why a dataset column needs no type of its own: a cell reaches an attribute as text and is parsed there, failing exactly as the same text typed by hand would. ## Shorthands & derived attributes Two pages hang beneath this one. `Attribute shorthands` covers the four ways an attribute is written shorter than `key: value` — the implicit `:value`, bare booleans and `!` negation, spaced names, and aliases. `Derived attributes` covers the attributes a construct computes while it renders — read-only, reached with `#id.name` or `{{#id.name}}`, never set. --- # Attribute shorthands The four ways an attribute can be written shorter than key: value — implicit values, bare booleans, ! negation and aliases. ## :value, the implicit attribute `:value` in the head fills the one attribute the element names for it — its *implicit attribute*. Every reference page marks which one that is, and an element that names none has no `:` form: `divider:x` is an error, not a value. The value is a simple token: it ends at the first bracket, whitespace, or end of line, so anything longer is written in the group instead. ```chalk image:photo.jpg(caption: Fig 1) image(source: photo.jpg; caption: Fig 1) curve:x^2(colour: blue) curve(expression: x^2; colour: blue) ``` A node is still recognised only by its bracket, so the shorthand does not remove the need for one: `image:photo.jpg` on its own is a paragraph, and `image:photo.jpg()` is the node. Micronodes and directives carry their own sigil and stand bare — `@include:overview.part.chalk` needs no bracket. ## Boolean shorthand An attribute typed `bool` may be written as a bare name, with no `:` and no value, and `!` before the name sets it false. `curve(dashed)` is `curve(dashed: true)`; `curve(!dashed)` is `curve(dashed: false)`. The negated form reaches attributes that default to true — `show-ticks`, `show-axes`, `show-grid` and `show-background` among them. Written out, a `bool` takes `true`, `false`, `yes` or `no`, in any case. `1` and `0` are not booleans: they are numbers, and admitting them here would make `bool` and `number` accept the same text, which is the one thing a union of the two may not do. ```chalk curve(expression: x^2; dashed) canvas.graph{ … }(show-grid; !show-ticks) ``` > The `!` here is not the micronode sigil. In an attribute group a leading `!` on a name negates a boolean; in prose or a value it opens a micronode. The position tells them apart, so `hidden: false`, `!hidden` and `hidden: true !cue.to{false}(at: 2)` are three different statements about the same attribute. ## Hyphens and case in attribute names An attribute name is an identifier. Written as a key, it folds internal whitespace to a single hyphen and matches case-insensitively, so `x-domain`, `x domain`, `X-DOMAIN` and `X Domain` all set the same attribute. ```chalk canvas.graph{ … }(x domain: [0, 5]) sets x-domain canvas.graph{ … }(Show-Grid) sets show-grid: true ``` The folding applies to keys and to `{{ }}` substitutions, not to bindings. A binding carries the name through to the renderer exactly as written, so a hyphenated attribute must be referred to by its canonical spelling — `#id.text-size` resolves and `#id.text size` does not. Every reference page lists the canonical name, and it is the one to write in every position. Both spellings compile, so which to write is a convention: `Source style` takes the spaced form in a group and the canonical one everywhere else. ## Aliases Many attributes accept more than one spelling: `colour` and `color`, `segment`'s `start` and `from`, `curve`'s `expression`, `exp`, `f`, `func` and `function`. Aliases are declared by the element, not general rewriting, so a reference page's attribute table is the list of what that element takes. --- # Derived attributes Read-only values a construct computes while it renders, the constructs that publish them, and their refusal of substitution. ## Read-only, recomputed every frame Some constructs compute something as they render — a fit's slope, an integral's area, the value a reader has dragged a parameter to. Those results are published as **derived attributes**: read-only, recomputed every frame, and never written by the author. They are listed in their own section on the reference page of any construct that has them. ## Reading a derived value By member access on the construct's id, written as a `{{…}}` group: ```chalk The fitted slope is {{#f.slope}}. ``` A reference written *bare* reaches one only where the attribute's declared type is `reference` — `fit(of: #points)`, `cue.trace(path: #flight)`, `table(data: #islands)`. Everywhere else the text `#f.slope` is just those characters: a mark's `label:` is `rich-text`, so `label: slope = #f.slope` renders the literal words *slope = #f.slope* rather than a number. The group works there, though. A `rich-text` attribute takes a bound expression exactly as prose does, whole or mixed into a sentence, so a mark may caption itself with a number it computes — including its own: ```chalk text(x: 0; y: 4; label: slope = {{round(#f.slope, 2)}}) curve#c(expression: sin(x); label: peaks at {{#c.max}}) ``` A bare reading shows about four significant figures. `{{round(#f.slope, 2)}}` fixes the decimals, and the text functions shape it further — `!format:percent{ {{#f.r2}} }` for a percentage. Whatever the sentence needs, it is done to the number in the expression rather than asked for as an attribute. ## Why it must stay a binding Reading `#f.slope` makes the expression around it **bound**: it depends on something that does not exist until the construct renders, so the compiler cannot finish it. A snapshot spliced into the source before parsing — reading the value once, while compiling — would let the prose assert a number the picture beside it later contradicts, so there is no such form. The expression tree travels to the renderer instead and is evaluated there, every frame. > The same is true of animated attributes and reader-set parameters: if a value can change after compilation, reading it makes the expression bound, and there is no way to freeze it early. `Identifiers and references` covers the distinction between a reference and a constant in full, and `Reading computed values in prose` the judgement that follows from it. ## Every derived attribute - `fit` — `slope`, `intercept`, `r2`. The slope is the fitted polynomial's derivative at zero: exact, and live under whichever degree is in force. - `curve` — `max` and `min` over the plotted domain. - `integral` — `value`, the computed area. - `parameter` and `cue.parameter` — `value`, where the letter currently stands: the reader's setting, or the scene's sweep where they have not moved it. - `contour` and `heatmap` — `min` and `max` over the visible window. - `boxplot` — `median`, `q1`, `q3`, `iqr`, and the sample's `min` and `max`. A box summarising a pair of samples publishes each axis's under that axis's name — `x-median`, `y-q3`, `im-iqr` — and no plain `median`, which a sample of pairs does not have. A box grouped by a column of names publishes the plain ones only while it holds one group: each name has its own median, so a number prose needs to cite is drawn as its own box, `on:` its name. - `choropleth` — `min`, `max`, `mean` and `count` over the values the join actually covered. - `arc` — `distance`, the great-circle length in kilometres. Each is published by the construct itself, from inside the renderer, once per frame. Nothing an author writes adds a derived attribute to a construct that does not declare one, and nothing sets one — an assignment is an error rather than a silent override: ```chalk fit#f(of: #points; model: polynomial; slope: 2) ``` Rejected by the compiler: `[attribute]` node `fit`: 'slope' is derived and cannot be set Existence is still proven at compile time: a reference to a derived name the construct does not publish fails before anything renders. --- # Comments Two ways to leave source out of the output: // for a line, @ignore for a block. ## // at the start of a line A line whose first non-whitespace characters are `//` is removed before anything else reads the source. It leaves nothing behind — no paragraph, no blank block. ```chalk scene{ // the fit degree here is deliberate — see the note below A straight line captures the trend. } ``` Only at the start of a line. `//` following any other content is ordinary text: `Real text. // trailing` compiles to a paragraph reading *Real text. // trailing*. ## @ignore for a block `@ignore` takes a body and drops all of it — the way to comment out a construct rather than a line. ```chalk @ignore{ curve(expression: x^3; colour: grey) } ``` ## Comments inside lines{} Inside a `lines` chunk the body is literal code, so `//` is interpreted by the environment's language rather than by Chalk. A JavaScript comment reaches the rendered environment unchanged. --- # Document header The starred group at the top of every file — the title, description and tags a document carries. ## Writing the header A document opens with a starred attribute group — `*` at the start of a line, followed by `( )`. It is the first thing in the file, it carries no body, and it is the one construct written with the `*` sigil. ```chalk *( title: The 68–95–99.7 rule, live description: However you set k, the band from mu - k sigma to mu + k sigma holds the same share. tags: statistics, probability, normal-distribution lesson ) scene{ … } ``` Inside, it is an ordinary attribute group: `key: value` pairs, one per line or separated by semicolons, with the `boolean shorthand` for the flags — `lesson`, not `lesson: true`. ## The header attributes The header is typed like any attribute group. `title` is the one required key — a document without one does not compile — and a key outside the set below is a header error, not something carried along. - `title` — the document's name, shown wherever it is listed or linked. Required. Alias `name`. It also names the subject every scene answers to — see `What a document covers`. - `description` — one sentence, used for cards, previews and search results. A card clamps it — four lines, fewer in a dense listing — so the part of a long description that decides whether to open the document has to be in the first line of it. - `tags` — a comma-separated list, used for subject browsing. Deduplicated, and capped at seven. - `lesson` — flags structured teaching material. Filterable under Explore's Lessons. - `template` — renders the Template badge in the title header, nothing more. - `private` — the document is visible only to its author and the people it is shared with. For a document published from a GitHub repository this is the one visibility control there is: a push to the default branch publishes, and there is no draft state. ## Placement At the top of the document, once. A partial spliced in with `@include` is source, not a document of its own, so it carries no header — the metadata belongs to the file that is being compiled. Below it, a document holds two nodes: `scene`, which is the document, and `cite`, which declares a source and renders nowhere. Anything else at that level is a content-policy error. Directives are the exception, having no placement rules at all, so a `@data` or `@let` may stand between scenes. --- # scene One of the two nodes a document holds directly, and the only one that renders where it is written. The body is the narrative — prose, headings, callouts — and the detail declares the canvas: the environments, and the block content shown beside them. A document is a sequence of scenes, read in order. - Kind: Node - Section: The document - Aliases: `scn` - Writing with it: What a document covers, Prose & canvas state, The final pass ## The scene A scene is a body of narrative and a detail that declares the canvas. A document is a sequence of scenes, read in order. The scene's own attributes govern its animation timeline — `duration` (its implicit attribute, `auto` by default), `autoplay`, and `loop`. ```chalk scene{ # A heading Narrative prose, read top to bottom. }[ canvas.graph{ curve(expression: sin(x)) } ] ``` Reference: `scene`, `step` ## Narrative and canvas The body `{ }` holds what is read: paragraphs, headings, lists, equations, callouts, dividers, references. The detail `[ ]` holds what is shown: canvas environments (`canvas.graph`, `canvas.code`) and supporting canvas content such as `table` and `image`. Most of the content vocabulary is legal in both — prose, the four headings, lists and equations read the same in either slot. What is not: the four callouts (`definition`, `note`, `example`, `task`) and the `internote` reference are narrative only, and `table`, `image`, `parameter`, the cues and the canvas environments are detail only. Each construct's reference page carries its own *Allowed in* row. > A detail can be opened full screen, without its narrative beside it — so what is on the canvas has to hold as a picture on its own. `Selecting canvas content` covers what that asks of the marks. ## Steps A `step` — written as `===` on its own line in the narrative — carves the scene's timeline into steps. Everything animated anchors to those steps: a cue's `in: 2` fires at step two, a `!cue.to`'s `at: 2` starts its move there. The reader's progress through the text advances the canvas. A marker is therefore a timeline trigger rather than a paragraph break — paragraphs separate with a blank line — so a scene whose detail never changes carries none at all, and a scene with markers has something anchored to each of them. `Prose and canvas state` gives the check. ```chalk scene{ First the data alone. === Then the fitted line. }[ canvas.graph{ scatter#points(x: 1, 2, 3; y: 2.0, 2.4, 3.1) cue{ fit(of: #points; model: polynomial; degree: 1) }( in: 1 ) } ] ``` ## Environments and splits A detail may declare two canvas environments; the scene's `direction` attribute arranges them — `auto` (the default), `horizontal` side by side, `vertical` stacked — and `ratio` gives the first environment's share over the second's, written as a fraction like `3/2`. `auto` follows the space the canvas has: stacked while the note is being read, where the canvas column is far taller than it is wide and two side-by-side halves of it are unreadable, and side by side in presentation, where the canvas has the whole screen and height is what is scarce. Leave it at `auto` unless the arrangement itself carries meaning. ```chalk scene{ A graph beside its code. }[ canvas.graph#g{ curve(expression: x^2) } canvas.code{ lines{ y = x ** 2 } }( language: python ) ]( ratio: 3/2 ) ``` > Write `horizontal` or `vertical` only when the split has a semantic reason to hold the same axis everywhere — a number line reading under the graph it indexes is `vertical` in presentation too, and a before/after pair stays `horizontal` in the reading view. A fixed direction chosen for how it happened to look in one of the two is the case `auto` exists for. ## Continuing an environment An environment carries an id on its node — `canvas.graph#projectile{...}` — and a later scene's environment can point back at it with `continue: #projectile`. What continues is the *session state*: reader-manipulated parameters, the fit degree they chose, the view they zoomed to. `continue:` hands off state, never markup — the content is the new environment's own. > Ids share one document-wide namespace and resolve forwards and backwards — a `continue:` may point at an environment declared in any scene, and a collision between any two `#id`s anywhere is an error. ## Attributes - `duration` — Total duration for animations or 'auto'. - Implicit - Type: `duration-auto` - Shorthand: `scene:value` - Default: `auto` - `autoplay` — Automatically play animations. - Type: `boolean` - Default: `true` - `loop` — Loop animations continuously. - Type: `boolean` - Default: `false` - `direction` — Split arrangement when the detail declares two canvas environments: horizontal (side by side) or vertical (stacked). - Options: `horizontal`, `vertical` - Default: `auto` - `ratio` — The first environment's share of a split over the second's, e.g. 3/2. Only meaningful with two canvas environments. - Type: `ratio` ## Allowed content - Body `{ }`: `paragraph`, `h1`, `h2`, `h3`, `h4`, `list`, `equation`, `definition`, `example`, `note`, `task`, `internote`, `divider`, `step` - Detail `[ ]`: `paragraph`, `h1`, `h2`, `h3`, `h4`, `list`, `equation`, `table`, `image`, `parameter`, `cue`, `cue.draw`, `canvas.graph`, `canvas.code`, `canvas.geo`, `canvas.diagram` ## Examples ```chalk scene{ # Title Intro paragraph. } ``` ```chalk scene{ # With Detail Explain the concept. }[ canvas.code{ lines{ print("hello") } }( language: python ) ] ``` --- # step A step marker that advances the scene's step timeline. Everything on the canvas — cue reveals, `!cue.to`, environment choreography — anchors to the steps it carves out of the narrative. - Kind: Node - Section: The document - Aliases: `cut` - Writing with it: Prose & canvas state ## Allowed in - `scene` — body `{ }` ## Examples ```chalk === ``` --- # cite One source the document draws on, declared beside the scenes rather than inside one. It renders nothing where it is written: the reference list is generated from every `cite` the document holds, and `!cite`:`#id`() in prose is what points at this one. The head carries the identifier the citations use — `cite#fs1969(…)`. - Kind: Node - Section: The document - Also a micronode: `cite` - Writing with it: Checking a document's claims ## Attributes - `authors` — The authors as they are cited, printed in the reference list exactly as written: `Fellegi, I. P. and Sunter, A. B.` Not a semicolon between two names — that ends the attribute. - Required - Type: `string` - Also written: `author` - `year` — The year of publication. Written text, not a number: `n.d.`, `in press` and `2019a` are all years a bibliography carries. - Type: `string` - `title` — The work's own title. - Required - Type: `string` - `container` — What the work appeared in — a journal, a book, a conference. Set in italic in the reference list. - Type: `string` - Also written: `journal`, `in` - `publisher` — The publishing body, for a book or a report. - Type: `string` - `pages` — Volume, issue and page range as one written locator: `64(328), 1183–1210`. - Type: `string` - Also written: `volume` - `doi` — The DOI, bare or as `doi:…`. Rendered as a link through doi.org. - Type: `string` - `url` — Where the work is, for a source with no DOI. Used only when `doi` is absent. - Type: `string` - Also written: `link` ## Examples ```chalk cite#fs1969( authors: Fellegi, I. P. and Sunter, A. B. year: 1969 title: A theory for record linkage container: Journal of the American Statistical Association pages: 64(328), 1183–1210 doi: 10.1080/01621459.1969.10501049 ) ``` ```chalk cite#tufte2001( authors: Tufte, E. R. year: 2001 title: The visual display of quantitative information publisher: Graphics Press ) ``` --- # cite A citation in prose: a numbered badge that opens the note's reference drawer at the source it names. `ref` points at a `cite` declaration in the same document, so a citation of a source that was never declared is a compile error rather than a gap in the list. The number is assigned in citation order — the first source cited is 1. The empty brackets are required: a micronode head carrying an implicit value is prose unless a `{` or `(` follows it (§1.5). - Kind: Micronode - Section: The document › cite - Also a node: `cite` - Writing with it: Checking a document's claims ## Attributes - `ref` — The `#id` of the `cite` declaration this cites. - Required, Implicit - Type: `reference` - Shorthand: `cite:value` - `locator` — Where in the source the claim is — `p. 1187`, `§4`, `fig. 2`. Shown in the badge beside the number; a property of this citation, not of the source. - Type: `string` - Also written: `at`, `page` ## Examples ```chalk As !cite:#fs1969() showed, the two files can be matched probabilistically. ``` ```chalk The bound is tight for small samples !cite:#nb2007(locator: p. 214). ``` --- # divider A horizontal rule, written as exactly `---` on its own line. Older spellings like `____` or a longer run of dashes no longer parse. - Kind: Node - Section: Content › Text blocks ## Allowed in - `scene` — body `{ }` ## Examples ```chalk --- ``` --- # h1 The largest heading, written `# ` at the start of a line. Headings sit inside a scene's narrative and hold rich text, so `$maths$` and inline formatting set in them. A document's title is not a heading — it comes from `title:` in the document header. An h1 opening the first scene is styled as the document's opening heading. - Kind: Node - Section: Content › Text blocks - Writing with it: Prose & headings ## Allowed in - `scene` — body `{ }` - `scene` — detail `[ ]` - `definition` — body `{ }` - `example` — body `{ }` - `note` — body `{ }` - `task` — body `{ }` - `task` — detail `[ ]` - `cue` — body `{ }` - `cue.draw` — body `{ }` ## Examples ```chalk # Falling objects ``` ```chalk # Fitting $y = mx + c$ ``` --- # h2 The second heading level, written `## ` at the start of a line. Holds rich text, as all four levels do. - Kind: Node - Section: Content › Text blocks - Writing with it: Prose & headings ## Allowed in - `scene` — body `{ }` - `scene` — detail `[ ]` - `definition` — body `{ }` - `example` — body `{ }` - `note` — body `{ }` - `task` — body `{ }` - `task` — detail `[ ]` - `cue` — body `{ }` - `cue.draw` — body `{ }` ## Examples ```chalk ## Reading the residuals ``` ```chalk ## What $r^2$ hides ``` --- # h3 The third heading level, written `### ` at the start of a line. Holds rich text, as all four levels do. - Kind: Node - Section: Content › Text blocks - Writing with it: Prose & headings ## Allowed in - `scene` — body `{ }` - `scene` — detail `[ ]` - `definition` — body `{ }` - `example` — body `{ }` - `note` — body `{ }` - `task` — body `{ }` - `task` — detail `[ ]` - `cue` — body `{ }` - `cue.draw` — body `{ }` ## Examples ```chalk ### A worked example ``` ```chalk ### Choosing the degree ``` --- # h4 The smallest heading level, written `#### ` at the start of a line. Holds rich text, as all four levels do. - Kind: Node - Section: Content › Text blocks - Writing with it: Prose & headings ## Allowed in - `scene` — body `{ }` - `scene` — detail `[ ]` - `definition` — body `{ }` - `example` — body `{ }` - `note` — body `{ }` - `task` — body `{ }` - `task` — detail `[ ]` - `cue` — body `{ }` - `cue.draw` — body `{ }` ## Examples ```chalk #### Notes on rounding ``` ```chalk #### Edge cases ``` --- # list A list. Items opening with `- ` are unordered, items opening with `1. ` ordered; each item holds rich text. - Kind: Node - Section: Content › Text blocks ## Allowed in - `scene` — body `{ }` - `scene` — detail `[ ]` - `definition` — body `{ }` - `example` — body `{ }` - `note` — body `{ }` - `task` — body `{ }` - `task` — detail `[ ]` - `cue` — body `{ }` ## Examples ```chalk - First - Second ``` ```chalk 1. Step one 2. Step two ``` --- # paragraph A block of prose, written bare — no marker opens it. Holds inline content: formatting, maths, links, colours and micronodes. - Kind: Node - Section: Content › Text blocks - Writing with it: Prose & headings ## Attributes - `colour` — Text colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `neutral` ## Allowed in - `scene` — body `{ }` - `scene` — detail `[ ]` - `definition` — body `{ }` - `example` — body `{ }` - `note` — body `{ }` - `task` — body `{ }` - `task` — detail `[ ]` - `cue` — body `{ }` - `cue.draw` — body `{ }` ## Examples ```chalk This is a paragraph. ``` ```chalk You can write **formatted** inline text like $x^2$. ``` --- # equation A block-level LaTeX equation. Its body is TeX source, set on a line of its own — `$…$` is the inline form of the same notation. - Kind: Node - Section: Content › Maths - Aliases: `eq` ## Attributes - `colour` — Equation colour. - Implicit - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Shorthand: `equation:value` - Default: `neutral` ## Allowed content - Body `{ }`: TeX math source (block equation content) ## Allowed in - `scene` — body `{ }` - `scene` — detail `[ ]` - `definition` — body `{ }` - `example` — body `{ }` - `note` — body `{ }` - `task` — body `{ }` - `task` — detail `[ ]` - `cue` — body `{ }` - `cue.draw` — body `{ }` ## Examples ```chalk equation{ E = mc^2 } ``` ```chalk equation{ \int_0^1 x^2 \; dx = \frac{1}{3} }( colour: blue ) ``` --- # latex A LaTeX expression set inside a sentence, written as $expression$. The equation element is the same notation given a line of its own. A micronode with a marker spelling rather than the `!name{}` form; it takes no attributes. - Kind: Micronode - Section: Content › Maths ## Examples ```chalk $x^2 + y^2 = r^2$ ``` ```chalk $\frac{d}{dx}(x^2)=2x$ ``` --- # definition A callout naming a term. `term:` is the implicit attribute — the word being defined — and the body holds the definition. The other three callouts take `title:` instead. - Kind: Node - Section: Content › Blocks & references - Aliases: `def` - Writing with it: Callouts ## Attributes - `term` — The term being defined. Rich text, so a term as much notation as word — `$\epsilon$-$\delta$ continuity` — sets as one. - Implicit - Type: `rich-text` - Also written: `name` - Shorthand: `definition:value` ## Allowed content - Body `{ }`: `paragraph`, `h1`, `h2`, `h3`, `h4`, `list`, `equation` ## Allowed in - `scene` — body `{ }` ## Examples ```chalk definition{ A variable stores a value. } ``` ```chalk definition{ Elasticity measures responsiveness. }( term: Elasticity ) ``` --- # example A callout holding a worked example. `title:` is the implicit attribute, and the body takes block content. - Kind: Node - Section: Content › Blocks & references - Writing with it: Callouts ## Attributes - `title` — Example title. Rich text, so `$maths$` and inline formatting render. - Implicit - Type: `rich-text` - Shorthand: `example:value` ## Allowed content - Body `{ }`: `paragraph`, `h1`, `h2`, `h3`, `h4`, `list`, `equation` ## Allowed in - `scene` — body `{ }` ## Examples ```chalk example{ x = 2 gives f(x) = 4. } ``` ```chalk example{ If price rises, quantity demanded falls. }( title: Law of Demand ) ``` --- # image An image. `source` is the implicit attribute and takes an uploaded asset or a URL; `caption` prints beneath it. Canvas content — legal in a scene's detail, not in its narrative body. - Kind: Node - Section: Content › Blocks & references - Aliases: `img` ## Attributes - `source` — Image URL or path. - Required, Implicit - Type: `string` - Also written: `src` - Shorthand: `image:value` - `caption` — A caption under the figure, and the image's alt text. Rich text, so `decay of $N_0$` sets the maths. - Type: `rich-text` ## Allowed in - `scene` — detail `[ ]` - `cue` — body `{ }` ## Examples ```chalk image(source: graph.png) ``` ```chalk image(source: https://example.com/plot.png; caption: Demand curve) ``` --- # internote A reference to another internote, rendered as a card on its own line. `ref` names the target document. The inline form of the same construct is a chip inside a sentence. - Kind: Node - Section: Content › Blocks & references - Also a micronode: `internote` ## Attributes - `ref` — Target internote ID. - Required, Implicit - Type: `string` - Also written: `internote` - Shorthand: `internote:value` - `connect` — Record the reference as a link between the two documents, so the target shows it among its connections. True by default; set false to cite without connecting. - Type: `boolean` - Default: `true` ## Allowed in - `scene` — body `{ }` ## Examples ```chalk internote(ref: abc123def456) ``` ```chalk internote(ref: xyz789; connect: false) ``` --- # note A callout for something set aside from the main line of argument. `title:` is the implicit attribute, and the body takes block content. - Kind: Node - Section: Content › Blocks & references - Writing with it: Callouts ## Attributes - `title` — Note title. Rich text, so `$maths$` and inline formatting render. - Implicit - Type: `rich-text` - Shorthand: `note:value` ## Allowed content - Body `{ }`: `paragraph`, `h1`, `h2`, `h3`, `h4`, `list`, `equation` ## Allowed in - `scene` — body `{ }` ## Examples ```chalk note{ This convention is important. } ``` ```chalk note{ Units must be consistent. }( title: Warning ) ``` --- # table A table. The body is pipe-delimited rows with inline Chalk in the cells, or nothing at all when `data:` binds a dataset instead. Header and footer rows and columns are switchable, and rows, columns or single cells can be highlighted. - Kind: Node - Section: Content › Blocks & references - Aliases: `tbl` ## Attributes - `data` — A `@data` dataset supplying the rows, in place of a body — `table:#survey`. The same rows can then feed a table, a mark and a code environment without being typed once per consumer. - Implicit - Type: `reference` - Shorthand: `table:value` - `columns` — Which columns of `data:` to show, and in what order — plain column names, not references, so no `#`. Omit for every column in the dataset's own order. - Type: `string-list` - `header-row` — Has header row. With `data:`, the dataset's column names are that row. - Type: `boolean` - Also written: `header` - Default: `true` - `header-column` — Has header column. - Type: `boolean` - Also written: `header-col` - Default: `false` - `footer-row` — Has footer row. - Type: `boolean` - Default: `false` - `footer-column` — Has footer column. - Type: `boolean` - Also written: `footer-col` - Default: `false` - `highlight-rows` — Row indices to highlight. - Type: `index-list` - Also written: `highlight-row` - `highlight-columns` — Column indices to highlight. - Type: `index-list` - Also written: `highlight-cols`, `highlight-col`, `highlight-column` - `highlight-cells` — Cells to highlight, comma-separated. A single reference is a column letter and a row number (`B2`); a range joins two with a hyphen (`A1-C3`) and covers the block between them. - Type: `cell-list` - Also written: `highlight-cell` - `highlight-colour` — Highlight colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `highlight-color` - Default: `blue` - `delimiter` — Column delimiter character. - Type: `single-char` - Also written: `delim` - Default: `|` - `title` — A caption above the table. Inline Chalk, so `Yield at $T = 300K$` sets the maths. - Type: `rich-text` - Also written: `caption` - `mode` — How the table presents: `inline` for a static display in place, `environment` to expand it into the canvas. - Options: `inline`, `environment` - Default: `inline` ## Allowed content - Body `{ }`: Pipe-delimited table rows with inline Chalk formatting in cells — or none at all, when `data:` supplies the rows from a dataset ## Allowed in - `scene` — detail `[ ]` ## Examples ```chalk table{ Name | Score Alice | 92 Bob | 88 }( header-row: true ) ``` ```chalk table{ x | y 0 | 0 1 | 1 }( title: Sample Data highlight-rows: 2 ) ``` ## Notes - `highlight-rows` and `highlight-columns` are 1-based and count every row and column drawn, the header among them — so with `header-row` on, the first row of data is row 2. - Every row needs the same number of delimiters as the header: rows are split on the delimiter and nothing is padded, so a row missing one renders short and its cells sit under the wrong headings. A cell with no value keeps its delimiters — write a placeholder in it rather than leaving the line to end early. --- # task A callout posing an exercise for the reader. `title:` is the implicit attribute, and the body takes block content. The optional detail is the held-back half — a hint or a solution — rendered inside the callout, collapsed until revealed. - Kind: Node - Section: Content › Blocks & references - Aliases: `exercise` - Writing with it: Callouts ## Attributes - `title` — Task title. Rich text, so `$maths$` and inline formatting render. - Implicit - Type: `rich-text` - Shorthand: `task:value` ## Allowed content - Body `{ }`: `paragraph`, `h1`, `h2`, `h3`, `h4`, `list`, `equation` - Detail `[ ]`: `paragraph`, `h1`, `h2`, `h3`, `h4`, `list`, `equation` ## Allowed in - `scene` — body `{ }` ## Examples ```chalk task{ Solve for x. } ``` ```chalk task{ Compute the derivative of x^3. }( title: Practice ) ``` ```chalk task{ Finish the loop. }[ The guard goes first, the update beneath it. ]( title: Practice ) ``` --- # bold Bold text, written as **text**. A micronode with a marker spelling rather than the `!name{}` form; it takes no attributes. - Kind: Micronode - Section: Content › Formatting ## Examples ```chalk **Bold text** ``` ```chalk Use **key term** in a sentence. ``` --- # footnote A footnote attached to a run of prose. The body is the marker text carried in the sentence, and `content` is the note itself. - Kind: Micronode - Section: Content › Formatting - Aliases: `fn` ## Attributes - `content` — Footnote content. - Required, Implicit - Type: `string` - Also written: `c` - Shorthand: `footnote:value` ## Allowed content - Body `{ }`: Inline Chalk content (formatted text, inline math, links, and inline elements) ## Examples ```chalk !fn{Source}(content: Smith, 2024) ``` ```chalk A claim !fn{note}(content: additional detail). ``` --- # highlight Highlighted text — a background tint behind a run of prose. The colour is the implicit attribute, so it is written as shorthand: `!h:red{Text}`. The ==shorthand== marks text in the default yellow. - Kind: Micronode - Section: Content › Formatting - Aliases: `h` ## Attributes - `colour` — Highlight colour. - Implicit - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Shorthand: `highlight:value` - Default: `yellow` ## Allowed content - Body `{ }`: Inline Chalk content (formatted text, inline math, links, and inline elements) ## Examples ```chalk !h:yellow{Important} ``` ```chalk Use !h:blue{this term} in context. ``` ```chalk ==marked== ``` --- # inline-code A run of prose set in the monospace face, written as `code` — variable names, function calls and short fragments inside a sentence. A micronode with a marker spelling rather than the `!name{}` form; it takes no attributes. - Kind: Micronode - Section: Content › Formatting ## Examples ```chalk `const x = 5` ``` ```chalk Use `npm run dev` to start. ``` --- # italic Italic text, written as *text*. A micronode with a marker spelling rather than the `!name{}` form; it takes no attributes. - Kind: Micronode - Section: Content › Formatting ## Examples ```chalk *Italic text* ``` ```chalk Use *emphasis* sparingly. ``` --- # link A hyperlink around a run of prose. `href` is the implicit attribute and takes an absolute URL or a site-relative path. - Kind: Micronode - Section: Content › Formatting - Aliases: `a` ## Attributes - `href` — Destination URL for the link. - Required, Implicit - Type: `string` - Also written: `url` - Shorthand: `link:value` ## Allowed content - Body `{ }`: Inline Chalk content (formatted text, inline math, links, and inline elements) ## Examples ```chalk !link{Open Docs}(href: https://example.com) ``` ```chalk See !link{this page}(href: /about). ``` --- # symbol A small glyph set in a line of prose, drawn at the size and colour of the text around it. Written `!symbol:tick()`: `name` is the implicit attribute, so `!symbol(name: tick)` is the same thing spelled out. Three kinds of thing are in the set: the marks a piece of work is marked with (`tick`, `cross`, `dash`, `question`, `star`, `warning`, `info`); the nouns a worded problem is about — people and places (`person`, `people`, `house`, `business`, `school`), everyday things (`book`, `clock`, `money`, `car`), science and nature (`tree`, `leaf`, `drop`, `sun`, `bolt`, `flask`, `globe`, `lightbulb`), and direction or change (`arrow-right`, `arrow-left`, `arrow-up`, `arrow-down`, `rise`, `fall`); and the four card suits (`spade`, `heart`, `diamond`, `club`), which a probability question reaches for. `heart` is the suit and the liking both. Every symbol has a spoken reading, so an internote read aloud keeps the sentence. - Kind: Micronode - Section: Content › Formatting ## Attributes - `name` — Which symbol is drawn. - Required, Implicit - Options: `tick`, `cross`, `dash`, `question`, `star`, `warning`, `info`, `arrow-right`, `arrow-left`, `arrow-up`, `arrow-down`, `rise`, `fall`, `person`, `people`, `house`, `business`, `school`, `book`, `clock`, `money`, `car`, `tree`, `leaf`, `drop`, `sun`, `bolt`, `flask`, `globe`, `lightbulb`, `spade`, `heart`, `diamond`, `club` - Shorthand: `symbol:value` - `colour` — Colour of the symbol. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `neutral` ## Examples ```chalk Correct: !symbol:tick(colour: green) ``` ```chalk Each !symbol:person() pays !symbol:money() rent on a !symbol:house(). ``` ```chalk Drawing a !symbol:heart(colour: red) has chance $13/52$. ``` --- # Functions The two ways to take a value and give one back: arithmetic inside {{…}}, and the six text micronodes that shape characters rather than quantity. ## Two kinds, and when each runs There are two ways to take a value and get one back, and they differ by *when* the work happens and by *what* the value is. - **Expression functions** run in the compiler, inside `{{…}}`. `{{round(p, 2)}}` is arithmetic, and the answer is a number. There are twenty-six of them and they are covered on `Expressions`. - **Text functions** run in the renderer. Six micronodes — `format`, `upper`, `lower`, `title`, `truncate`, `pad` — do what arithmetic cannot, because they are about characters rather than quantity. There used to be a third, `!value`, which read a live number and formatted it. It is gone: an expression reads a live number anywhere `!value` could — prose and `rich-text` attributes alike — and does arithmetic besides, and the shaping it carried as attributes is what the text functions do. > These used to be eleven compile-time *folds* — micronodes the compiler evaluated and spliced into the surrounding text as characters. They are gone. The five numeric ones came back as expression functions, so `!round:2{…}` is now written `{{round(…, 2)}}`; the six text ones came back as ordinary micronodes with the same spellings. If you are reading content written before this, see `Expressions` for the numeric rewrites. ## Numbers: round, ceil, floor, abs, clamp Rounding, bounding and the rest of the arithmetic live inside a group. They are evaluated while the document compiles, so what reaches the page is a number and nothing else — no node, no identifier, nothing a renderer has to know about. ```chalk {{round(3.14159, 2)}} 3.14 {{clamp(1.4, 0, 1)}} 1 {{abs(-4.5)}} 4.5 {{ceil(3.2)}} 4 {{floor(3.8)}} 3 ``` Because an expression is arithmetic all the way down, these compose with everything else in one place rather than nesting micronodes: `{{round(100 * #f.r2, 1)}}`. And because an expression may read a reference, the old rule that a function could not see a live number is gone — such a group simply does not finish at compile time, and the renderer evaluates it. ## Text: format, upper, lower, title, truncate, pad Case, separators, lengths and padding are not arithmetic, so they have no expression form. They are micronodes, declared by this library and applied when the page renders. - `format` — separators. `comma` groups in threes, `percent` scales by 100 and appends the sign, `fixed` uses `places` without grouping, `scientific` writes a mantissa and exponent. - `upper`, `lower` and `title` — case. These keep any markup inside them, so `!upper{**loud**}` is still bold. - `truncate` — to a maximum `length`, ending in an `ellipsis` counted inside it. - `pad` — widened to `length` with a filler, at the `start` or the `end`. ```chalk !format:comma{1234567} 1,234,567 !upper{hello} HELLO !truncate:8{internationalisation} interna… !pad:6{42}(with: 0) 000042 ``` Each names its implicit attribute, so `!truncate:8{…}` and `!truncate{…}(length: 8)` are the same thing. `upper`, `lower` and `title` take no attributes and so have no `:` form. `truncate` and `pad` measure characters, so they flatten their body to plain text first: a length in characters has no meaning across a bold span. Lengths count Unicode scalar values. > `format` emits separators and **never a currency symbol**. There is no locale table behind it — the grouping is the document's, so the same page reads the same way everywhere — and the symbol therefore belongs to the sentence: `$!format:comma{1234.57}(places: 2)`. ## Reading a live number A number the canvas computes is read with an expression, and shaped by the two kinds of function together — the arithmetic inside the group, the characters outside it: ```chalk The fitted slope is {{#f.slope}}. Rounded, {{round(#f.slope, 2)}}. The fit explains {{round(100 * #f.r2, 1)}}% of the variance. The sample holds !format:comma{ {{#total.value}} } readings. ``` A text function reads the current value of any expression in its body, so the two compose rather than competing: the expression says which number and what to do to it, the function says how it is written. `Reading computed values in prose` covers when a sentence should be quoting a computed number at all. > One thing the old `!value` did has no replacement: `maths: true` set the number in maths type, to sit beside a `$…$` run. A `$…$` body is decided while the document compiles, so it cannot hold a value that does not exist yet — a live number beside maths renders in body type. --- # format Writes a number with separators — `!format:comma{1234567}` gives `1,234,567`. Separators only, never a currency symbol: the symbol belongs to the sentence, not the number, so the author writes `$!format:comma{…}`. - Kind: Micronode - Section: Content › Functions - Explained in: Functions ## Attributes - `as` — Which form to write: comma groups the integer part in threes, percent scales by 100 and appends %, fixed uses places with no grouping, and scientific writes mantissa and exponent. - Required, Implicit - Options: `comma`, `percent`, `scientific`, `fixed` - Shorthand: `format:value` - `places` — Decimal places. Omitted, comma and percent keep the value's own decimals, fixed uses two, and scientific writes five mantissa decimals. - Type: `number` ## Allowed content - Body `{ }`: Inline Chalk content, read as a number ## Examples ```chalk The survey counted !format:comma{1234567} plants. ``` ```chalk It covers !format:percent{0.4567}(places: 1) of the island. ``` ## Notes - `!format:comma{1234567}` gives `1,234,567`; `!format:percent{0.4567}(places: 1)`, `45.7%`; `!format:fixed{3.1}`, `3.10`; `!format:scientific{1234.5}`, `1.23450e3`. - There is no locale table behind this: the grouping is the document's, so the same page reads the same way everywhere. - A body that is not a number is left as it stands. The compiler used to refuse one; it no longer sees this at all, so the honest render is the author's own characters rather than `NaN`. --- # lower Lowers text to lower case. Markup inside is kept. - Kind: Micronode - Section: Content › Functions - Explained in: Functions ## Allowed content - Body `{ }`: Inline Chalk content ## Examples ```chalk !lower{{header-key}} ``` ## Notes - `!lower{HeLLo}` gives `hello`. --- # pad Widens text to a fixed length by repeating a filler character. Text already at or beyond the length is unchanged. - Kind: Micronode - Section: Content › Functions - Explained in: Functions ## Attributes - `length` — The length to pad up to. - Required, Implicit - Type: `number` - Shorthand: `pad:value` - `with` — The filler. Absent or empty, it is one space — a string value is trimmed and cannot carry one. - Type: `string` - Default: `` - `side` — Which end the filler goes on: start or end. - Options: `start`, `end` - Default: `start` ## Allowed content - Body `{ }`: Inline Chalk content, flattened to plain text ## Examples ```chalk !pad:6{42}(with: 0) ``` ## Notes - `!pad:6{42}` gives ` 42`; `!pad:6{42}(side: end; with: .)`, `42....`; `!pad:6{42}(with: 0)`, `000042`. - The filler is trimmed by the attribute parser and so cannot be a space — which is why an absent or empty `with` means one space. --- # title Raises the first letter of each word and lowers the rest. Markup inside is kept. - Kind: Micronode - Section: Content › Functions - Explained in: Functions ## Allowed content - Body `{ }`: Inline Chalk content ## Examples ```chalk !title{{island-name}} ``` ## Notes - `!title{the quick brown fox}` gives `The Quick Brown Fox`. - A plain rule, with no small-word exceptions — those would need to know the language. --- # truncate Shortens text to a maximum length, ending it with an ellipsis. Length counts Unicode scalar values, and the ellipsis counts inside it, so the result is never longer than asked. - Kind: Micronode - Section: Content › Functions - Explained in: Functions ## Attributes - `length` — Maximum length of the result, ellipsis included. - Required, Implicit - Type: `number` - Shorthand: `truncate:value` - `ellipsis` — What marks the cut. - Type: `string` - Default: `…` ## Allowed content - Body `{ }`: Inline Chalk content, flattened to plain text ## Examples ```chalk !truncate:8{internationalisation} ``` ## Notes - `!truncate:8{internationalisation}` gives `interna…`; text already within the length is unchanged. - A `length` with no room for the ellipsis drops the ellipsis rather than overflowing the length. --- # upper Raises text to upper case. Markup inside is kept, so `!upper{**loud**}` is still bold. - Kind: Micronode - Section: Content › Functions - Explained in: Functions ## Allowed content - Body `{ }`: Inline Chalk content ## Examples ```chalk !upper{{species-code}} ``` ## Notes - `!upper{hello}` gives `HELLO`. --- # canvas.graph A plotting environment on the scene's canvas. Its body holds marks — scatter, curve, line, the shapes, fit and the surfaces — and the parameters that drive them, each of which gets a slider in the environment footer. It draws its own frame, and the x and y domains are animatable with `!cue.to`. - Kind: Node - Section: Graph environment - Writing with it: Selecting canvas content, Framing a readable graph ## The graph environment `canvas.graph` is a plotting surface: its body holds marks, it draws its own frame, and its domains are animatable. It carries the environment's id — `canvas.graph#projectile{...}` — and the frame is set by `x-domain` / `y-domain` (derived from the data when omitted), `x-axis-label` / `y-axis-label`, `show-grid`, `show-axes`, `show-ticks`, and `show-background` as the master switch. `show-ticks: false` hides the axis's own numbers and nothing else — `axis.label` names stay, and the gutter shrinks to what is left in it. Two more the frame carries. `aspect: square` takes the largest square the margins leave and centres the plot in it — a square **frame**, not a square unit. Leaving both domains off already gives a picture whose x unit and y unit are the same length on screen, which is right where both axes carry the same thing and wrong where they do not: a quantile-quantile plot is read by whether its points lie along the 45° line, and only a square frame makes that fair, while its two axes are a sample against a distribution and must keep their own scales. Under `aspect: square` they do, and the two never share a grid step. And `seed` is the one number every settled pseudo-random thing the graph draws comes from — today the offsets of a `scatter(layout: jitter)`. Which noise a jitter uses is arbitrary; that it is the same noise on every render, in every browser and in three years is not, so the offsets are a function of the seed and the reading's own row and of nothing else. Change the seed and the same sample is arranged differently, which is how a cloud that happens to hide a point is fixed without touching the data. ## Planes A graph's head names its **plane** — what a coordinate on its marks is. `canvas.graph:xy` is the ordinary plane and the default; `canvas.graph:x` a number line, with one axis and marks that take `x` alone; `canvas.graph:argand` the complex plane, whose axes are `re` and `im` and whose marks take one complex `z` — `point(z: 3 + 2i)`, `point(z: (3 + 2i)^2)`, with `i` a constant and every expression complex-valued — or `re` and `im` apart, which is how two columns bind; `canvas.graph:polar`, whose axes are `r` and `theta`, the angle in radians unless written `60deg`. A pair — a segment's ends, an angle's corner — is `(a, b)` on every plane, read in the plane's own axes. ```chalk canvas.graph:argand{ vector(z: 3 + 2i; label: $z$) point(z: (3 + 2i)^2; label: $z^2$) circle(z: 0; size: abs(3 + 2i); fill: false) curve(z: sqrt(13) * e^(i * t); t: [0, 2pi]) axis.label(im: 2; label: $2i$) }( re domain: [-4, 14] im domain: [-6, 14] ) ``` ```chalk canvas.graph:polar{ curve(expression: 1 + cos(theta)) point(r: 2; theta: 60deg) axis.band(r: [1, 2]) axis.band(theta: [0, pi/4]) }( r domain: [0, 3] ) ``` A plane admits the marks it can draw and nothing else — the compiler says which when one is refused, and why. The argand plane has no independent axis, so `fit`, `integral` and `curve: f` are not drawn there (a curve on it is `z(t)`); the polar plane refuses `integral`, whose area is not the one it computes; the number line draws positions and series and nothing that needs a second axis. Each mark's page says where it is drawn. The same rules hold for a mark inside a `cue`. Every plane's axis attributes are spelled with the axis's name in front — `re domain:`, `r domain:`, `theta format:` — and the other planes' are refused. ## Axes An axis has two properties, written with its name in front and combining with each other and with any plane. `x format:` is what the axis *holds*: `numbers`; `radians`, which sets each label as a fraction of π and rules the axis in π — its automatic grid step comes off the π ladder, π/4, π/2, π, 2π; `degrees`, whose values are still radians and whose labels read `30°`; `dates`, which places `2024`, `2024-03`, `2024-03-15` and a date column at their fractional years and ticks by span; the preset series `months` and `weekdays` (ticks `Jan`…, `Mon`…); and a written list — `x format: Red, Blue, Yellow` — or a dataset column — `x format: #sales.region`, its distinct cells in row order. A series makes the axis categorical: the names stand at positions 1…n, every mark on the axis speaks them (`line(x: Mon, Tue, Wed; y: 12, 19, 15)`), a column of names binds directly, and a name the axis does not have is a compile error. A continuous mark — `curve`, `fit`, `integral` — is refused on a categorical axis: there is nothing between two names to draw it over. `auto`, the default, is decided from the marks: a categorical series on the axis rules it in those names, in the order they first appear; a date column or literal rules it in dates; otherwise the axis holds numbers. Write the values out when the order or the full set matters — a Likert scale, a category with no data that must still appear. `x scale:` is how the axis *places* a value: `linear`, `log` (v at log₁₀ v) or `log2`. A transform, not a relabelling — the data is written as measured, `scatter(x: 10, 100, 1000)`, and the axis puts each reading at its logarithm: curves are sampled in it, a `fit` on log–log gives the exponent as its slope, domains and `!cue.zoom` windows stay in data units, and the ticks are the decades written as values. A literal at or below zero is a compile error; a cell at or below zero drops its row. A scale beside names, dates or angles is refused, and it is never `auto`. ```chalk canvas.graph{ scatter#readings(x: 10, 100, 1000; y: 3, 9, 27) fit(of: #readings; degree: 1) }( x scale: log y scale: log ) ``` ```chalk canvas.graph{ @data#sales{ region | y2023 | y2024 North | 38.0 | 42.1 South | 33.5 | 31.4 East | 47.2 | 55.0 West | 26.1 | 27.8 } bar(x: #sales.region; y: #sales.y2023; colour: grey; label: 2023) bar(x: #sales.region; y: #sales.y2024; colour: blue; label: 2024) }( y axis label: revenue (£k) ) ``` ## Marks Everything drawn on the surface is a mark, and marks share a set of attributes: `colour`, `label` (rich text, so `$maths$` sets properly), `hidden` (declared but not drawn — and animatable), `lock` (protects the mark on an `editable:` graph), and `layer` (paint order). Every outline takes `dashed`, which is how a figure draws the shape that is *not* there. Each mark's own page opens with what it draws and how to write it; by what they draw, they are: - **Positions and figures** — `point`, `line`, `segment`, `vector`, `polygon`, and the `shapes`, which differ only in outline. - **Notation** — `angle` marks and measures the angle at a vertex, `brace` spans a distance and names it, and `text` sets words anywhere in the picture. - **Functions** — `curve` plots an expression or a position in `t`; `integral` shades the area under one; `contour` and `heatmap` draw a function of `x` and `y`. - **Readings** — `scatter` and `lollipop` draw readings at positions, `bar` stands one at each name, and `histogram` bins a sample on a numeric axis. Every one binds a dataset column directly; the `@data` page covers where columns come from. - **Summaries of a sample** — `boxplot`, `violin` and `density` take the readings themselves and work the summary out. - **Models** — `fit` lays a least-squares line or polynomial through a scatter, and `residuals` draws each reading's distance from it. - **Axis annotations** — `axis.label` names a value on an axis, `axis.brace` names an interval beside it, and `axis.band` shades one across the plot. ## Where marks stand Some marks draw a sample rather than a position, and those *stand*. On the number line a `scatter`, a `boxplot` and a `violin` each take a lane above the line, in source order, the first sitting on the line. On a plane a mark stands at a place on the other axis. Written with one coordinate — `x:` and it lies along the x axis, `y:` and it stands up the y axis — its `on:` says where. A number there stands it at that value; a name stands it at that name and rules the axis in the names its marks stand at, so three samples side by side need no `x format:` line. The room at a place is the band there — a unit on an axis of names, and on a continuous one the closest two places stand, so nothing at one can overlap anything at another. Everything standing at one place splits that room in source order, as several `bar` marks split the band at one name; `width:` is each mark's share of its slot. ```chalk canvas.graph:xy{ boxplot(y: #trial.control; on: control; colour: blue) scatter(y: #trial.control; on: control; colour: grey) boxplot(y: #trial.treated; on: treated; colour: orange) scatter(y: #trial.treated; on: treated; colour: grey) }( y axis label: mass (g) ) ``` A box, a violin and a scatter of the *same readings* are one sample drawn several ways, and share one lane or slot rather than splitting it: the summary lands over the readings with nothing to position by hand, and which is drawn over which is source order and `layer`. Written with *both* coordinates and one axis ruled in names, a mark stands at every name at once, each reading at the name in its own row — the shape data usually arrives in, one row per reading and a column saying which group it belongs to. The names are drawn at the edge of the plot whatever the other axis's window holds: an axis ruled in names never crosses at the origin, so a diverging plot centred on zero keeps its names clear of the marks. ## Derived attributes Marks that compute something publish it for reading: a curve's `max` and `min`, a fit's `slope`, `intercept` and `r2`, an integral's `value`, a surface's `min` and `max`, a boxplot's `median`, `q1`, `q3`, `iqr`, `min` and `max` (under each axis's name where it summarises a pair of samples), a parameter's `value`. Reach one by member access on the mark's id — `#f.slope` in an attribute, `{{#f.slope}}` in prose. `Derived attributes` covers how they behave. ## Attributes On any plane: - `plane` — What a coordinate on the marks IS: `x` a number line, `xy` the ordinary plane (the default), `argand` the complex plane (`re`/`im`, or one complex `z`), `polar` (`r`/`theta`). The head form — `canvas.graph:polar{…}`. A plane admits the marks it can draw and withdraws the other planes' axis attributes; a mark inside a cue is held to it just the same. - Implicit - Type: `plane` - Shorthand: `canvas.graph:value` - Default: `xy` - `title` — The environment's accessible name — read by a screen reader, never drawn (the axis labels name a graph on the page). Rich text, so `$maths$` and inline formatting render. A named attribute: the head `:` names the plane. - Type: `rich-text` - `editable` — The reader may add their own elements, and pan or zoom the frame. - Type: `boolean` - Default: `false` - `interactive` — Let the reader explore: scroll to zoom, drag to pan, double-click to reset — and zoom / reset controls appear in the toolbar. - Type: `boolean` - Default: `false` - `show-ticks` — Show tick labels. - Animatable - Type: `boolean` - Also written: `ticks` - Default: `true` - `show-axes` — Show the axes. - Animatable - Type: `boolean` - Also written: `axes` - Default: `true` - `show-background` — Master switch for the frame (grid, axes, ticks). - Animatable - Type: `boolean` - Also written: `background`, `show-bg`, `bg` - Default: `true` - `show-grid` — Draw the grid — the lines or dots the marks are measured against; on the polar plane, the rings at the r ladder and the rays at the θ one. - Animatable - Type: `boolean` - Also written: `grid` - Default: `true` - `grid-style` — What the grid is made `of:` 'lines' is squared paper, 'dots' steps back and lets the marks carry the picture. The -minor variants add unlabelled subdivisions between the labelled lines. - Animatable - Options: `lines`, `lines-minor`, `dots`, `dots-minor` - Also written: `grid-type` - Default: `lines` - `seed` — The number every settled pseudo-random thing this graph draws comes from — today, the offsets of a `scatter(layout: jitter)`. Which noise a jitter uses is arbitrary; that it is the same noise every time is not, since a picture that rearranged itself between visits would have the reader looking for meaning in the rearrangement, and a document whose figures are not the same twice cannot be cited. So each offset is a function of this number and the reading's own row and of nothing else — no clock, no generator carrying state, nothing that differs between one machine and another. Which leaves the seed as an authoring control: the same readings at `seed: 2` are the same sample arranged differently, so a jittered cloud that happens to hide a point is fixed by changing one number rather than by editing the data. - Type: `number` - Default: `0` - `continue` — Inherit an earlier same-type environment's state: `continue: #that-environment`. - Type: `reference` Only with x plane: - `x-domain` — X range, e.g. [0, 10]. Derived from the data when omitted. Animatable: `x-domain: [0, 5] !cue.to{[0, 10]}(at: 1; over: 800ms)`. For a magnification, `!cue.zoom` draws a box around the new window and moves the frame into it: `x-domain: [0, 10] !cue.zoom{[2, 4]}(at: 2; draw: 700ms; hold: 250ms)`. Endpoints may use parameters — `y-domain:` [0, 0.46 / s] frames the environment live. On a categorical axis the ends are names: `[Tue, Thu]`. The ordinary plane and the number line; the other planes name their own axes (`re-domain`, `r-domain`…). - Animatable - Type: `axis-interval` - Also written: `domain`, `x-range` - `x-format` — What the x axis holds. `numbers`; `radians` (ruled in π — the automatic grid step comes off the π ladder, ticks set as fractions of π); `degrees` (values in radians, ticks written `30°`); `dates` (ISO literals `2024`, `2024-03`, `2024-03-15` and date columns at their fractional years, ticks by span); the preset series `months` and `weekdays` (ticks `Jan`…, `Mon`…; 1 = January, 1 = Monday); a written list (`Red, Blue, Yellow`) or a dataset column (`#sales.region`, its distinct cells in row order). A series makes the axis categorical: the names stand at positions 1…n, every mark on the axis speaks them, a column of names binds directly, a name the axis does not have is a compile error, and a continuous mark — `curve`, `fit`, `integral`, `contour`, `heatmap`, `residuals` — is refused. `auto`, the default, is decided from the marks: a categorical series rules the axis in its names, a date column or literal makes it `dates`, otherwise `numbers`. A categorical axis is drawn at the plot's edge, the y axis at the left and the x axis at the bottom, never through the origin, so its names stay clear of the marks. - Animatable - Type: `axis-format` - Also written: `x-labels` - Default: `auto` - `x-scale` — How the x axis places a value: `linear`, `log` (v at log₁₀ v) or `log2`. A transform, not a relabelling: curves are sampled in it, a fit on log–log gives the exponent as its slope, `axis.band(x: [100, 1000])` is one decade, domains and `!cue.zoom` windows stay in data units, ticks are the decades written as values (`10`, `100`, `10⁴` past ten thousand). A literal ≤ 0 is a compile error; a cell ≤ 0 drops its row. Refused beside names, dates or angles. - Type: `axis-scale` - Default: `linear` - `x-grid-spacing` — Grid step on x, or auto. - Animatable - Type: `grid-step` - Also written: `x-grid`, `x-spacing` - Default: `auto` - `x-axis-label` — X axis name. - Type: `rich-text` - Also written: `x-label` Only with xy plane: - `x-domain` — X range, e.g. [0, 10]. Derived from the data when omitted. Animatable: `x-domain: [0, 5] !cue.to{[0, 10]}(at: 1; over: 800ms)`. For a magnification, `!cue.zoom` draws a box around the new window and moves the frame into it: `x-domain: [0, 10] !cue.zoom{[2, 4]}(at: 2; draw: 700ms; hold: 250ms)`. Endpoints may use parameters — `y-domain:` [0, 0.46 / s] frames the environment live. On a categorical axis the ends are names: `[Tue, Thu]`. The ordinary plane and the number line; the other planes name their own axes (`re-domain`, `r-domain`…). - Animatable - Type: `axis-interval` - Also written: `domain`, `x-range` - `y-domain` — Y range. Derived from the data when omitted. Animatable, with `!cue.to` or `!cue.zoom` — write the same anchor on both domains and the two axes move as one window. Endpoints may use parameters — `y-domain:` [0, 0.46 / s] frames the environment live. On a categorical axis the ends are names: `[Tue, Thu]`. The ordinary plane and the number line; the other planes name their own axes (`re-domain`, `r-domain`…). - Animatable - Type: `axis-interval` - Also written: `range`, `y-range` - `x-format` — What the x axis holds. `numbers`; `radians` (ruled in π — the automatic grid step comes off the π ladder, ticks set as fractions of π); `degrees` (values in radians, ticks written `30°`); `dates` (ISO literals `2024`, `2024-03`, `2024-03-15` and date columns at their fractional years, ticks by span); the preset series `months` and `weekdays` (ticks `Jan`…, `Mon`…; 1 = January, 1 = Monday); a written list (`Red, Blue, Yellow`) or a dataset column (`#sales.region`, its distinct cells in row order). A series makes the axis categorical: the names stand at positions 1…n, every mark on the axis speaks them, a column of names binds directly, a name the axis does not have is a compile error, and a continuous mark — `curve`, `fit`, `integral`, `contour`, `heatmap`, `residuals` — is refused. `auto`, the default, is decided from the marks: a categorical series rules the axis in its names, a date column or literal makes it `dates`, otherwise `numbers`. A categorical axis is drawn at the plot's edge, the y axis at the left and the x axis at the bottom, never through the origin, so its names stay clear of the marks. - Animatable - Type: `axis-format` - Also written: `x-labels` - Default: `auto` - `y-format` — What the y axis holds — as `x-format`. A categorical axis is drawn at the plot's edge, the y axis at the left and the x axis at the bottom, never through the origin, so its names stay clear of the marks. - Animatable - Type: `axis-format` - Also written: `y-labels` - Default: `auto` - `x-scale` — How the x axis places a value: `linear`, `log` (v at log₁₀ v) or `log2`. A transform, not a relabelling: curves are sampled in it, a fit on log–log gives the exponent as its slope, `axis.band(x: [100, 1000])` is one decade, domains and `!cue.zoom` windows stay in data units, ticks are the decades written as values (`10`, `100`, `10⁴` past ten thousand). A literal ≤ 0 is a compile error; a cell ≤ 0 drops its row. Refused beside names, dates or angles. - Type: `axis-scale` - Default: `linear` - `y-scale` — How the y axis places a value — as `x-scale`. - Type: `axis-scale` - Default: `linear` - `x-grid-spacing` — Grid step on x, or auto. - Animatable - Type: `grid-step` - Also written: `x-grid`, `x-spacing` - Default: `auto` - `y-grid-spacing` — Grid step on y, or auto. - Animatable - Type: `grid-step` - Also written: `y-grid`, `y-spacing` - Default: `auto` - `x-axis-label` — X axis name. - Type: `rich-text` - Also written: `x-label` - `y-axis-label` — Y axis name. - Type: `rich-text` - Also written: `y-label` - `transform` — The map the plane is drawn through. On `xy` it is a 2×2 matrix written as its ROWS — `(1, 1), (0, 1)` is (x, y) ↦ (x + y, y), and its columns are where the basis vectors land; entries may be parametised, `(1, k), (0, 1)`. On `argand` it is a function of `z` — `z^2`, `1/z`, `(z + 1/z)/2`. Every position goes through it and the grid is drawn as the image of the grid; the frame does not move, since the window, its ticks and its numbers measure the space the picture is mapped into. An auto domain is settled once, over the marks and their images under every map the graph will pass through, so the window holds the whole animation from the start. Refused beside a mark that reads an axis rather than a position (`bar`, `histogram`, `density`, `boxplot`, `violin`, `lollipop`, `fit`, `residuals`, `integral`, `contour`, `heatmap`) and beside `editable:`. - Animatable - Type: `graph-transform` - `aspect` — The shape of the plot region: 'auto' fills the room the environment gives it, 'square' takes the largest square that room holds and centres it there. A square FRAME, not a square unit — leaving both domains off already gives a picture whose x unit and y unit are the same length on screen, which is right where the two axes carry the same thing (a symmetry plot, both axes in dollars) and wrong where they do not. A quantile-quantile plot is read by whether its points lie along the 45° line, and that judgement is only fair on a square frame; its axes are a sample against a distribution, so tying their units together would be a claim about the data. Under 'square' each axis goes on framing its own readings, and the two never share a grid step. R's par(pty = "s"). Withdrawn on the number line, which is a band as tall as its lanes need, and on the polar plane, whose disc is already drawn square. - Options: `auto`, `square` - Default: `auto` Only with argand plane: - `re-domain` — The real axis's range on the argand plane, e.g. [-4, 14]. Derived from the data when omitted; animatable with `!cue.to` and `!cue.zoom` like `x-domain`. - Animatable - Type: `axis-interval` - Also written: `re-range` - `im-domain` — The imaginary axis's range on the argand plane. Derived from the data when omitted; animatable like `y-domain`. - Animatable - Type: `axis-interval` - Also written: `im-range` - `re-grid-spacing` — Grid step on the real axis, or auto. - Animatable - Type: `grid-step` - Also written: `re-grid`, `re-spacing` - Default: `auto` - `im-grid-spacing` — Grid step on the imaginary axis, or auto. - Animatable - Type: `grid-step` - Also written: `im-grid`, `im-spacing` - Default: `auto` - `re-axis-label` — The real axis's name — `Re` unless given. - Type: `rich-text` - Also written: `re-label` - `im-axis-label` — The imaginary axis's name — `Im` unless given. - Type: `rich-text` - Also written: `im-label` - `transform` — The map the plane is drawn through. On `xy` it is a 2×2 matrix written as its ROWS — `(1, 1), (0, 1)` is (x, y) ↦ (x + y, y), and its columns are where the basis vectors land; entries may be parametised, `(1, k), (0, 1)`. On `argand` it is a function of `z` — `z^2`, `1/z`, `(z + 1/z)/2`. Every position goes through it and the grid is drawn as the image of the grid; the frame does not move, since the window, its ticks and its numbers measure the space the picture is mapped into. An auto domain is settled once, over the marks and their images under every map the graph will pass through, so the window holds the whole animation from the start. Refused beside a mark that reads an axis rather than a position (`bar`, `histogram`, `density`, `boxplot`, `violin`, `lollipop`, `fit`, `residuals`, `integral`, `contour`, `heatmap`) and beside `editable:`. - Animatable - Type: `graph-transform` - `aspect` — The shape of the plot region: 'auto' fills the room the environment gives it, 'square' takes the largest square that room holds and centres it there. A square FRAME, not a square unit — leaving both domains off already gives a picture whose x unit and y unit are the same length on screen, which is right where the two axes carry the same thing (a symmetry plot, both axes in dollars) and wrong where they do not. A quantile-quantile plot is read by whether its points lie along the 45° line, and that judgement is only fair on a square frame; its axes are a sample against a distribution, so tying their units together would be a claim about the data. Under 'square' each axis goes on framing its own readings, and the two never share a grid step. R's par(pty = "s"). Withdrawn on the number line, which is a band as tall as its lanes need, and on the polar plane, whose disc is already drawn square. - Options: `auto`, `square` - Default: `auto` Only with polar plane: - `r-domain` — The polar plane's radius, e.g. [0, 3]: the outermost ring. Derived from the marks when omitted, snapped up to the r ladder. Under `r scale: log` the start is the radius at the centre. - Animatable - Type: `axis-interval` - Also written: `r-range` - `theta-domain` — The polar plane's sector, in radians — `[0, pi/2]` draws a quarter turn. The full turn when omitted. The window an r(theta) curve runs over. - Animatable - Type: `axis-interval` - Also written: `theta-range` - `theta-format` — What the polar plane's angle axis holds: `radians` (the default — rays at π/6, labels as fractions of π), `degrees` (rays every 30°, labelled `30°`), or a series of names (`North, East, South, West`, `weekdays`, a column) for a radar chart's spokes or a rose's sectors, standing at equal angles round the turn. - Animatable - Type: `axis-format` - Also written: `theta-labels` - Default: `auto` - `r-scale` — How the polar plane's radius is placed: `linear` or `log`/`log2`, which rings at the decades from the `r-domain`'s start outward. - Type: `axis-scale` - Default: `linear` - `r-grid-spacing` — The ring spacing, or auto. - Animatable - Type: `grid-step` - Also written: `r-grid`, `r-spacing` - Default: `auto` - `theta-grid-spacing` — The ray spacing in radians, or auto (π/6; one per name on a categorical theta). - Animatable - Type: `grid-step` - Also written: `theta-grid`, `theta-spacing` - Default: `auto` ## Allowed content - Body `{ }` — only with x plane: `point`, `text`, `axis.label`, `axis.brace`, `axis.band`, `segment`, `brace`, `vector`, `scatter`, `parameter`, `cue.parameter`, `boxplot`, `violin`, `cue`, `cue.draw`, `cue.trace`, `cue.highlight`, `cue.spotlight` - Body `{ }` — only with xy plane: `point`, `text`, `axis.label`, `axis.brace`, `axis.band`, `segment`, `brace`, `vector`, `scatter`, `parameter`, `cue.parameter`, `line`, `polygon`, `circle`, `square`, `rectangle`, `triangle`, `star`, `diamond`, `hexagon`, `angle`, `curve`, `fit`, `residuals`, `integral`, `bar`, `lollipop`, `histogram`, `density`, `boxplot`, `violin`, `contour`, `heatmap`, `cue`, `cue.draw`, `cue.trace`, `cue.highlight`, `cue.spotlight` - Body `{ }` — only with argand plane: `point`, `text`, `axis.label`, `axis.brace`, `axis.band`, `segment`, `brace`, `vector`, `scatter`, `parameter`, `cue.parameter`, `line`, `polygon`, `circle`, `square`, `rectangle`, `triangle`, `star`, `diamond`, `hexagon`, `angle`, `curve`, `boxplot`, `violin`, `contour`, `heatmap`, `cue`, `cue.draw`, `cue.trace`, `cue.highlight`, `cue.spotlight` - Body `{ }` — only with polar plane: `point`, `text`, `axis.label`, `axis.brace`, `axis.band`, `segment`, `brace`, `vector`, `scatter`, `parameter`, `cue.parameter`, `line`, `polygon`, `circle`, `square`, `rectangle`, `triangle`, `star`, `diamond`, `hexagon`, `angle`, `curve`, `bar`, `histogram`, `contour`, `heatmap`, `cue`, `cue.draw`, `cue.trace`, `cue.highlight`, `cue.spotlight` ## Allowed in - `scene` — detail `[ ]` ## Examples ```chalk canvas.graph#fit-graph{ scatter#observations(x: 1, 2, 3, 4; y: 2.0, 2.4, 3.1, 4.8; colour: grey) fit#trend(of: #observations; degree: 1 to 4; colour: teal) cue{ residuals(of: #trend; colour: orange) }( in: 1 ) }( x-axis-label: x y-axis-label: y ) ``` ## Notes - An environment's own id is written on the node — `canvas.graph#projectile{…}` — and `continue:` points at it with `#projectile`. --- # angle The angle at a vertex, drawn where it is and measured as it is drawn: the arc between two arms, the square where it is a right angle, and — on request — the reading itself. `angle(at: (1, 1); from: (4, 1); to: (1, 4))` marks the corner of a triangle. An arm is written as either of the two things an arm is: a point it runs through, `(4, 1)`, or a bearing in degrees anticlockwise from the positive x axis, `30`. The mark publishes derived `value` (degrees) and `radians`. - Kind: Node - Section: Graph environment › Marks - Only with xy, argand or polar plane An angle marks the angle at a vertex and measures it: `angle(at: (1, 1); from: (4, 1); to: (1, 4))` is the corner of a triangle, drawn with the arc — or, at a right angle, with the square a reader was taught to look for. An arm is written as either of the two things an arm is: a point it runs through, `(4, 1)`, or a bearing in degrees anticlockwise from the positive x axis, `30`. `sweep:` chooses which of the four ways round is meant — `minor` (the corner, and the default), `reflex`, or the signed `anticlockwise` / `clockwise` pair a sector or a rotation needs. ```chalk canvas.graph{ polygon(x: 0, 4, 0; y: 0, 0, 3; colour: grey) angle#a(at: (0, 0); from: (4, 0); to: (0, 3)) angle#b(at: (4, 0); from: (0, 0); to: (0, 3); show-value: true; label: $\theta$) } ``` > An angle publishes derived `value` (degrees) and `radians`, and `show-value: true` draws the reading on the plot. Both are the angle *as drawn*, so a caption citing `{{#b.value}}` cannot disagree with the arc beside it — including while a keyframed arm is still sweeping round. ## Attributes - `at` — The corner — the point both arms come out of. Animatable as a whole, so `!cue.to{(2, 2)}` carries the mark to a new vertex in a straight line. - Required, Implicit, Animatable - Type: `parametised-coordinate-list` - Also written: `vertex`, `origin` - Shorthand: `angle:value` - `from` — One arm: a point it runs through, `(4, 1)`, or a bearing in degrees anticlockwise from the positive x axis, `30`. Either form may be an expression in the environment's parameters. Keyframed, the arm sweeps round to its new direction rather than jumping — and it goes the short way, so an arm asked to move 10° does not travel 350°. - Required, Animatable - Type: `angle-arm` - Also written: `a`, `start` - `to` — The other arm, written either of the ways `from` may be. The two need not agree: `from: (4, 1); to: 90` is perfectly good. - Required, Animatable - Type: `angle-arm` - Also written: `b`, `end` - `sweep` — Which of the four ways round the arms is meant. `minor` is the corner — the smaller of the two readings, never more than a straight line, which is what “the angle at that vertex” means everywhere outside a rotation. `reflex` is the way round outside it. `anticlockwise` and `clockwise` take the arms in the order they are written, which is what a sector or a turn needs: 240° anticlockwise is not 120° clockwise. - Options: `minor`, `reflex`, `anticlockwise`, `clockwise` - Also written: `direction` - Default: `minor` - `radius` — How big the marker is, in pixels. Furniture, like `width` and `text-size` and unlike a `circle`'s `size`: an angle mark is the same size on the page whether the plot spans two units or two million. - Animatable - Type: `parametised-number` - Default: `28` - `mark` — What is drawn at the corner. `auto` follows the convention every reader was taught with — the square at a right angle, the arc everywhere else. `arc` and `square` say it outright: a square on an angle that is not quite 90° is how a diagram states an assumption. - Animatable - Options: `auto`, `arc`, `square` - Also written: `marker` - Default: `auto` - `arcs` — Concentric arcs — one, two or three — the notation that marks two angles as equal to each other. Ignored where the marker is the right-angle square, which is marked once or not at all. - Type: `number` - Also written: `ticks` - Default: `1` - `arms` — Draw the arms themselves: out to the point where one was given, a short ray where a bearing was. Off by default, because an angle is usually marked between things the plot already draws. - Animatable - Type: `boolean` - Also written: `rays`, `legs` - Default: `false` - `show-value` — Draw the measurement, in degrees, at the arc — the protractor reading, live under a parameter, a keyframe and a moving vertex alike. With a `label:` as well it takes the line below it, so an angle can be named and measured in one place. - Animatable - Type: `boolean` - Also written: `show-measure`, `measure` - Default: `false` - `colour` — The arc, the wash behind it, the arms and the reading. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `purple` - `fill` — Wash the sector between the arms, behind the arc. - Animatable - Type: `boolean` - Also written: `filled` - Default: `true` - `width` — Stroke width of the arc, the square and the arms. - Animatable - Type: `number` - Default: `2` - `opacity` — Opacity. - Animatable - Type: `number` - Default: `1` - `label` — Text drawn just outside the arc, out along the bisector — rich text, so `$\theta$` sets as maths. Fades and draws with the element. - Type: `rich-text` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and unlike a `cue`, a hidden element still frames the plot, so the axes do not jump when it appears. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. Nothing at all on a graph that is not editable. - Type: `boolean` - Also written: `locked` - Default: `false` - `text-size` — Size of the label and the reading. - Animatable - Type: `number` - Default: `20` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` ## Derived attributes Computed while the element renders — read one with `#id.name` in attribute position or `{{#id.name}}` in prose, never set. - `value` — The angle as drawn, in degrees — the same fact the arc is making, live under a parameter, a keyframed arm and a moving vertex. - Derived - `radians` — The same angle in radians, for the documents whose maths is written that way. - Derived ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk angle(at: (1, 1); from: (4, 1); to: (1, 4)) ``` ```chalk angle#theta(at: (0, 0); from: 0; to: 60; arms: true; show-value: true) ``` ```chalk angle(at: (0, 0); from: 0; to: 240; sweep: anticlockwise; colour: teal) ``` ```chalk angle(at: (2, 3); from: (5, 3); to: (2, 6); arcs: 2; label: $\theta$) ``` ## Notes - Give an angle a node id — `angle#theta(…)` — and the prose beside the plot can cite its measurement with `{{#theta.value}}`, which cannot drift from the arc it is describing. --- # axis.band Shades an interval on an axis across the whole plot — a reference band the marks are read against: an operating range, the recession quarters, `$\pm\sigma$`. It draws under the grid and the marks. The axis is chosen by which interval is given, as with `axis.label`'s coordinate. - Kind: Node - Section: Graph environment › Marks An axis band shades an interval across the whole plot — a reference band the marks are read against, `axis.band(label: recession; x: [2008, 2009.5])` — drawn under the grid and the marks. Which axis it speaks to is chosen by which interval is given. On a log scale the interval is in data units, so `axis.band(x: [100, 1000])` is one decade. ## Attributes On any plane: - `label` — The name at the band's edge. Rich text; optional — a band says something with no words on it. - Implicit - Type: `rich-text` - Also written: `text`, `name` - Shorthand: `axis.band:value` - `colour` — Band and label colour. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `grey` - `opacity` — The wash's opacity. - Animatable - Type: `number` - Default: `0.15` - `edges` — Dashed hairline rules at the band's two edges. - Animatable - Type: `boolean` - Default: `false` - `size` — Label text size. - Animatable - Type: `number` - Also written: `text-size` - Default: `20` - `hidden` — Declared but not drawn. Animatable, for reveals. - Animatable - Type: `boolean` - Default: `false` Only with x plane: - `x` — The interval to shade along the x axis, e.g. [2008, 2009.5]. Give exactly one of x or y; either end may ride a parameter. On a categorical axis the ends are names, `[Tue, Thu]` — a band to a name covers that name’s whole share of the axis. - Animatable - Type: `axis-interval` Only with xy plane: - `x` — The interval to shade along the x axis, e.g. [2008, 2009.5]. Give exactly one of x or y; either end may ride a parameter. On a categorical axis the ends are names, `[Tue, Thu]` — a band to a name covers that name’s whole share of the axis. - Animatable - Type: `axis-interval` - `y` — The interval to shade along the y axis. On a categorical axis the ends are names, `[Tue, Thu]` — a band to a name covers that name’s whole share of the axis. - Animatable - Type: `axis-interval` Only with argand plane: - `re` — On the argand plane, the interval on the real axis; naming it selects the axis. - Animatable - Type: `axis-interval` - `im` — On the argand plane, the interval on the imaginary axis; naming it selects the axis. - Animatable - Type: `axis-interval` Only with polar plane: - `r` — On the polar plane, the interval on the r axis; naming it selects the axis. A band on r is an annulus. - Animatable - Type: `axis-interval` - `theta` — On the polar plane, the interval on the theta axis, in radians (`60deg` for degrees) or a name; naming it selects the axis. A band on theta is a sector. - Animatable - Type: `axis-interval` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk axis.band(label: recession; x: [2008, 2009.5]) ``` ```chalk axis.band(y: [-1, 1]; colour: teal; edges: true) ``` --- # axis.brace Names an interval on an axis with a curly brace, drawn in the gutter beside the tick numbers, so a span carries a name rather than two endpoints. The axis is chosen by which interval is given, as with `axis.label`'s coordinate. - Kind: Node - Section: Graph environment › Marks An axis brace names an interval: a curly brace spanning `x: [m, m + s]` with its label at the tip, drawn in the gutter beside the tick numbers. Which axis it speaks to is chosen by which interval is given. For a distance anywhere in the picture rather than along an axis, use a `brace`. ## Attributes On any plane: - `label` — The name at the brace's tip. Rich text, so $\sigma$ sets as maths. - Required, Implicit - Type: `rich-text` - Also written: `text`, `name` - Shorthand: `axis.brace:value` - `flip` — Draw on the opposite side of the axis from the tick numbers. - Animatable - Type: `boolean` - Default: `false` - `size` — Label text size, in the graph's points. The default is the tick numbers' size; a mark's label is 20. - Animatable - Type: `number` - Also written: `text-size` - Default: `16` - `width` — Stroke width. - Animatable - Type: `number` - Default: `1.5` - `colour` — Brace and label colour. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `neutral` - `hidden` — Declared but not drawn. Animatable, for reveals. - Animatable - Type: `boolean` - Default: `false` Only with x plane: - `x` — The interval to brace along the x axis, e.g. [m, m + s]. Give exactly one of x or y. On a categorical axis the ends are names, `[Tue, Thu]`. - Animatable - Type: `axis-interval` Only with xy plane: - `x` — The interval to brace along the x axis, e.g. [m, m + s]. Give exactly one of x or y. On a categorical axis the ends are names, `[Tue, Thu]`. - Animatable - Type: `axis-interval` - `y` — The interval to brace along the y axis. On a categorical axis the ends are names, `[Tue, Thu]`. - Animatable - Type: `axis-interval` Only with argand plane: - `re` — On the argand plane, the interval on the real axis; naming it selects the axis. - Animatable - Type: `axis-interval` - `im` — On the argand plane, the interval on the imaginary axis; naming it selects the axis. - Animatable - Type: `axis-interval` Only with polar plane: - `r` — On the polar plane, the interval on the r axis; naming it selects the axis. - Animatable - Type: `axis-interval` - `theta` — On the polar plane, the interval on the theta axis, in radians (`60deg` for degrees) or a name; naming it selects the axis. - Animatable - Type: `axis-interval` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk axis.brace(label: $\sigma$; x: [m, m + s]; colour: grey) ``` ```chalk axis.brace:$h$(y: [0, 4]; flip: true) ``` --- # axis.label A value on an axis, named: it puts a name where the tick number would have gone and removes the number — a period, a mean, a boundary, a solution. The axis is chosen by which coordinate is given, as a point takes x and y. `line: true` adds a rule across the plot at that value, under the marks. - Kind: Node - Section: Graph environment › Marks - Writing with it: Framing a readable graph An axis label names a value on an axis — `axis.label(x: 3.1416; label: $\pi$)` puts π where the number would have gone, and `line: true` adds a rule across the plot at that value. Which axis it speaks to is chosen by which coordinate is given. A name on the y axis is measured into the gutter beside it, so a worded one gets the room it needs; past about a quarter of the frame it wraps onto a second line rather than growing wider. The graph's `show-ticks: false` hides the axis's own numbers and leaves these names standing. ## Attributes On any plane: - `label` — The name itself — rich text, so `$\pi$` sets as maths. - Required, Implicit - Type: `rich-text` - Also written: `text`, `name` - Shorthand: `axis.label:value` - `line` — A rule across the plot at that value, drawn under the marks. - Animatable - Type: `boolean` - Also written: `rule`, `gridline` - Default: `false` - `dashed` — Whether that rule is dashed. - Type: `boolean` - Default: `true` - `size` — Text size, in the graph's points. The default is the tick numbers' size; a mark's label is 20. - Animatable - Type: `number` - Also written: `text-size` - Default: `16` - `colour` — The name, and its rule. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `neutral` - `hidden` — Declared and framed, but not drawn. - Animatable - Type: `boolean` - Default: `false` Only with x plane: - `x` — The value on the x axis to name — a number or expression, or a name on a categorical axis (`x: Wed`). Give this OR y, never both. - Animatable - Type: `axis-value` Only with xy plane: - `x` — The value on the x axis to name — a number or expression, or a name on a categorical axis (`x: Wed`). Give this OR y, never both. - Animatable - Type: `axis-value` - `y` — The value on the y axis to name — a number or expression, or a name on a categorical axis (`y: Wed`). Give this OR x, never both. - Animatable - Type: `axis-value` Only with argand plane: - `re` — On the argand plane, the place on the real axis; naming it selects the axis. - Animatable - Type: `axis-value` - `im` — On the argand plane, the place on the imaginary axis; naming it selects the axis. - Animatable - Type: `axis-value` Only with polar plane: - `r` — On the polar plane, the place on the r axis; naming it selects the axis. - Animatable - Type: `axis-value` - `theta` — On the polar plane, the place on the theta axis, in radians (`60deg` for degrees) or a name; naming it selects the axis. - Animatable - Type: `axis-value` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk axis.label(x: 3.1416; label: $\pi$) ``` ```chalk axis.label(y: 1; label: $1$; line: true; colour: teal) ``` ```chalk axis.label(x: 1.5708; label: $\tfrac{\pi}{2}$; line: true; dashed: false) ``` --- # bar Rectangles standing on one axis at the names of the other: `bar(x: North, South; y: 42, 31)` rules the x axis in those names and stands a bar at each. A series mark like `scatter` — `x` and `y` are read the same way and paired by position — and it is the categorical axis (`x-format`/`y-format`, or the `auto` rule) that makes it a bar chart. Exactly one axis must be categorical; the other is the value axis, which always reaches the baseline. Which axis is which is the orientation. Several bars at one name split the band in source order; `stack` stands a bar on the one before it. Not a histogram: a histogram’s width is data (its `bins` are edges), a bar’s is a share of a band. - Kind: Node - Section: Graph environment › Marks - Only with xy or polar plane A bar reads `x` / `y` the way a `scatter` does and stands a bar at each reading: one axis must be categorical (the axis of names is the orientation), the other reaches the baseline. Several bars at one name split the band in source order; `stack` stands a bar on the one before it instead; `width` is the share of the band the bars fill. On the polar plane a bar is a rose, standing at names on `theta`. ```chalk canvas.graph{ @data#sales{ region | y2023 | y2024 North | 38.0 | 42.1 South | 33.5 | 31.4 East | 47.2 | 55.0 West | 26.1 | 27.8 } bar(x: #sales.region; y: #sales.y2023; colour: grey; label: 2023) bar(x: #sales.region; y: #sales.y2024; colour: blue; label: 2024) }( y axis label: revenue (£k) ) ``` ## Attributes On any plane: - `z` — On the argand plane, the readings as complex numbers: written out (`1 + i, 2 - i, -3i`, a bare number a real), or a column of complex literals bound whole (`#roots.z`). Instead of `re`/`im`. - Animatable - Type: `complex-series` - `re` — On the argand plane, the readings' real parts, one per reading — numbers written out or a column — paired with `im` by position. - Animatable - Type: `series` - `im` — On the argand plane, the readings' imaginary parts, paired with `re` by position. - Animatable - Type: `series` - `period` — Long-format data: one row per name per period, this naming the period column (`period: #sales.year`). The bars then show the slice at whatever the environment's parameter over that column holds, gliding between the two periods that bracket it. Periods may be years, ISO dates, month or weekday names, `HH:MM` hours or `YYYY-Www` weeks. - Type: `reference` - Also written: `at` - `colours` — Colour for each bar, cycled in order — one colour colours the lot. If fewer colours than bars are given, the list starts again from the first. - Animatable - Type: `colour-list` - Also written: `colors`, `colour`, `color` - Default: `blue` - `opacity` — Opacity. - Animatable - Type: `number` - Default: `1` - `width` — The share of the band at each name that the bars fill, 0–1. Several bars at one name divide it between them. - Animatable - Type: `number` - Default: `0.8` - `stack` — Stand on top of the bar written before this one, in its share of the band, instead of beside it. - Type: `boolean` - Also written: `stacked` - Default: `false` - `show-labels` — Write each bar’s value at its end. - Animatable - Type: `boolean` - Also written: `labels` - Default: `false` - `label` — Text drawn beside the element — rich text, so `$maths$` and inline formatting render. Fades and draws with the element. - Type: `rich-text` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and unlike a `cue`, a hidden element still frames the plot, so the axes do not jump when it appears. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. Nothing at all on a graph that is not editable. - Type: `boolean` - Also written: `locked` - Default: `false` - `text-size` — Text size. - Animatable - Type: `number` - Default: `20` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` Only with xy plane: - `x` — The readings’ x values: names, numbers, or a dataset column (`#sales.region`). Paired with `y` by position; a pair either axis cannot read is dropped whole. - Implicit, Animatable - Type: `series` - Shorthand: `bar:value` - `y` — The readings’ y values, paired with `x` by position. - Animatable - Type: `series` Only with polar plane: - `r` — On the polar plane, the readings' distances from the origin, paired with `theta` by position. A rose reaches each bar out to its reading. - Animatable - Type: `series` - `theta` — On the polar plane, the readings' angles in radians (`60deg` for degrees), paired with `r` by position; names on a categorical theta. A rose stands its bars at names — `theta: North, East, South, West`, or `theta format:`. - Animatable - Type: `series` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` --- # boxplot A sample drawn as its five-number summary. Which coordinates are written says what is being summarised: one of them is one sample, and the box lies along the axis it is written on; both, with one axis ruled in names, are a sample grouped by those names, drawn as a box at every name; both numeric are one bivariate sample, paired reading by reading as a `scatter`'s are, drawn as the rectangle of the hinges with the two medians crossed inside it. `boxplot(x: 2, 3, 3, 4, 7, 9)` takes the readings themselves, the way a `scatter` does, and works the summary out: the box spans the quartiles with the median ruled across it, and the whiskers run out to the readings beyond them. The quartiles are Tukey's hinges — the median splits the ordered readings in two, itself in neither half when the count is odd, and each quartile is the median of its half — which is what a box plot has always drawn and what a class is taught to find by hand. A box STANDS, as a scatter does: in a lane above the number line, at its `on:` on a plane, or at each name of its names column. Everything standing at one place takes a share of the room there in source order — except that a box and a `scatter` of the *same readings* are one sample drawn twice and share one slot, on every plane, so writing both puts the summary over the dots with nothing to position by hand, and the box leaves its outlier dots to them. Not on the polar plane: a quartile is a value a quarter of the way along an axis, and the angle axis closes on itself. - Kind: Node - Section: Graph environment › Marks - Only with x, xy or argand plane ## The box and its readings A box takes the readings themselves and works the summary out: the box spans the quartiles, the median is ruled across it, and the whiskers run to the readings inside Tukey's fences, with whatever lies beyond drawn as its own dot (`whiskers: extremes` runs them to the smallest and largest instead, and `!outliers` keeps the fences but leaves those dots off — animatable, so a step can fade them in over a box the reader has already read). The quartiles are the hinges a class is taught to find — the median splits the ordered readings in two, itself in neither half when the count is odd, and each quartile is the median of its half. A box and a `scatter` of the *same readings* are one sample drawn twice and share one lane, so writing both puts the summary over the readings with nothing to position by hand: the dots swarm about the line the whiskers run along, and the box leaves its outlier dots to them, since those readings are dots already. Which is drawn over which is source order and `layer`, as for any pair of marks, so write the box first: its wash drawn over the dots would tint every one inside it. ```chalk canvas.graph:x{ boxplot#control(x: #trial.mass; colour: teal; label: control) scatter(x: #trial.mass; colour: grey) }( x axis label: mass (g) ) ``` > A boxplot exposes derived `median`, `q1`, `q3`, `iqr`, `min` and `max`, so prose citing `{{#control.iqr}}` reads the width of the box beside it. `min` and `max` are the sample's extremes whether or not a whisker reaches them. ## A box per group A box with an `on:` is that sample's summary, standing at that place on the other axis. Written with both coordinates and one axis ruled in names, a box stands at every name at once, each reading at the name in its own row — so the two columns data usually arrives in draw a box per group. A grouped box publishes its `median` and the rest only while it holds one group: each name has its own, and a derived number has one name per element, so a number prose needs to cite is drawn as its own box, `on:` its name. ## A box of paired readings Written with both coordinates numeric, a box is one bivariate sample, paired reading by reading as a scatter's are, and what it draws is the rectangle of the hinges — the x quartiles by the y quartiles — with the two medians crossed inside it, a whisker from each edge out to that axis's fence, and every reading outside either fence as its own dot. It is what a scatter looks like once it is summarised, and it composes with one: written over the same columns, the box lands on the very dots it summarises. A box of a pair of samples publishes its numbers under each axis's own name — `{{#cloud.x-median}}`, `{{#cloud.y-q3}}` — and no plain `median`, which a sample of pairs does not have. On the argand plane the pair is `re:` and `im:`, or one complex `z:`. ```chalk canvas.graph:xy{ boxplot(x: #trees.height; y: #trees.mass; colour: teal) scatter#cloud(x: #trees.height; y: #trees.mass; colour: grey) } ``` ## Attributes On any plane: - `whiskers` — How far the whiskers reach. `fences` stops each at the furthest reading within one and a half interquartile ranges of its quartile and draws whatever lies beyond as its own dot; `extremes` runs them to the smallest and largest readings, and then nothing stands out. - Animatable - Options: `fences`, `extremes` - Default: `fences` - `outliers` — Draw the readings past the fences. On by default: the whiskers stop where they stop either way, so a box with its outliers left off claims a reach the sample does not have and says nothing about the readings it dropped. `!outliers` takes them off anyway — for a scene about the middle of a distribution rather than its tails, and for one that puts them back on a step, since it is animatable (`outliers: false !cue.to{true}(at: 2)`). The frame still holds them, so fading them in does not move the axis. Nothing under `whiskers: extremes`, which has no reading outside its whiskers. Not drawn where the box shares its slot with a `scatter` of the same readings: every reading past the fences is one of those dots already. - Animatable - Type: `boolean` - Default: `true` - `colour` — Colour. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `blue` - `width` — The share of its room that the box is thick — its lane on the number line, its slot of the band on a plane, where the band is one unit on an axis of names and the closest two places stand on a continuous one. The same room a `scatter`'s `width:` is a share of, so a box and its dots are measured against one thing. The whisker caps are half of it, so one number sets the weight of the whole mark. Nothing to a box of a pair of samples, whose thickness is the spread of the second sample. - Animatable - Type: `number` - Default: `0.5` - `opacity` — Opacity. - Animatable - Type: `number` - Default: `1` - `label` — Text drawn beside the element — rich text, so `$maths$` and inline formatting render. Fades and draws with the element. - Type: `rich-text` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and unlike a `cue`, a hidden element still frames the plot, so the axes do not jump when it appears. It holds its slot the same way, drawn yet or not, so a box arriving on a step lands beside the marks already there rather than moving them. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. Nothing at all on a graph that is not editable. - Type: `boolean` - Also written: `locked` - Default: `false` - `text-size` — Text size. - Animatable - Type: `number` - Default: `20` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` Only with x plane: - `x` — The readings — numbers written out, or a dataset column bound whole (`#trial.mass`). The sample itself, not a summary of it: a summary written by hand cannot be checked against the sample it claims to summarise. A row that is not a number drops. - Implicit, Animatable - Type: `series` - Shorthand: `boxplot:value` Only with xy plane: - `x` — The readings — numbers written out, or a dataset column bound whole (`#trial.mass`). The sample itself, not a summary of it: a summary written by hand cannot be checked against the sample it claims to summarise. A row that is not a number drops. - Implicit, Animatable - Type: `series` - Shorthand: `boxplot:value` - `y` — The readings when the sample is read up the y axis, written out or bound whole (`#trial.mass`). One coordinate is one sample. `x:` and `y:` together, with one of the two axes ruled in names, are a sample grouped by those names — a box at every name over that name's rows; both numeric, they are the paired readings of one bivariate sample, row by row, and a row missing either half drops whole. - Animatable - Type: `series` - `on` — Where a one-sample box stands on the axis it does not summarise — a number, a name, or a date, as `axis.label` takes. Required on a plane for a box written with one coordinate, since the second axis is an axis and a box laid across it by lane would be asserting a coordinate its sample does not have. A name joins the axis's names, so boxes written `on: control` and `on: treated` rule the axis in those names with no `x format:` line, and a `scatter` with the same `on:` stands in the same place. Refused on the number line, whose boxes take a lane each; on a grouped box, whose names column already says where each box stands; and on a box of a pair of samples, which stands where its readings put it. - Animatable - Type: `axis-value` Only with argand plane: - `re` — The readings on the real axis. One of `re:` and `im:` alone is one sample; the pair is one bivariate sample, which `z:` writes as a single list. - Animatable - Type: `series` - `im` — The readings on the imaginary axis. One of `re:` and `im:` alone is one sample; the pair is one bivariate sample, which `z:` writes as a single list. - Animatable - Type: `series` - `z` — A bivariate sample as one list of complex readings, summarised on each axis in turn — `re:` and `im:` written apart are the same box. A complex column binds here, there being no way to split one into two real ones. - Animatable - Type: `complex-series` - `on` — Where a one-sample box stands on the axis it does not summarise — a number, a name, or a date, as `axis.label` takes. Required on a plane for a box written with one coordinate, since the second axis is an axis and a box laid across it by lane would be asserting a coordinate its sample does not have. A name joins the axis's names, so boxes written `on: control` and `on: treated` rule the axis in those names with no `x format:` line, and a `scatter` with the same `on:` stands in the same place. Refused on the number line, whose boxes take a lane each; on a grouped box, whose names column already says where each box stands; and on a box of a pair of samples, which stands where its readings put it. - Animatable - Type: `axis-value` ## Derived attributes Computed while the element renders — read one with `#id.name` in attribute position or `{{#id.name}}` in prose, never set. - `median` — The middle reading — the rule across the box. Published by a box holding one sample; a box grouped over several names publishes none. - Derived - `q1` — The lower quartile: the median of the lower half of the ordered readings. Published by a box holding one sample; a box grouped over several names publishes none. - Derived - `q3` — The upper quartile: the median of the upper half. Published by a box holding one sample; a box grouped over several names publishes none. - Derived - `iqr` — The interquartile range, `q3 − q1` — the width of the box. Published by a box holding one sample; a box grouped over several names publishes none. - Derived - `min` — The smallest reading in the sample, whether or not a whisker reaches it. Published by a box holding one sample; a box grouped over several names publishes none. - Derived - `max` — The largest reading in the sample, whether or not a whisker reaches it. Published by a box holding one sample; a box grouped over several names publishes none. - Derived - `x-median` — The middle reading — the rule across the box. Of the readings on the x axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `median`, which a sample of pairs does not have. - Derived - `x-q1` — The lower quartile: the median of the lower half of the ordered readings. Of the readings on the x axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `q1`, which a sample of pairs does not have. - Derived - `x-q3` — The upper quartile: the median of the upper half. Of the readings on the x axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `q3`, which a sample of pairs does not have. - Derived - `x-iqr` — The interquartile range, `q3 − q1` — the width of the box. Of the readings on the x axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `iqr`, which a sample of pairs does not have. - Derived - `x-min` — The smallest reading in the sample, whether or not a whisker reaches it. Of the readings on the x axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `min`, which a sample of pairs does not have. - Derived - `x-max` — The largest reading in the sample, whether or not a whisker reaches it. Of the readings on the x axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `max`, which a sample of pairs does not have. - Derived - `y-median` — The middle reading — the rule across the box. Of the readings on the y axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `median`, which a sample of pairs does not have. - Derived - `y-q1` — The lower quartile: the median of the lower half of the ordered readings. Of the readings on the y axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `q1`, which a sample of pairs does not have. - Derived - `y-q3` — The upper quartile: the median of the upper half. Of the readings on the y axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `q3`, which a sample of pairs does not have. - Derived - `y-iqr` — The interquartile range, `q3 − q1` — the width of the box. Of the readings on the y axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `iqr`, which a sample of pairs does not have. - Derived - `y-min` — The smallest reading in the sample, whether or not a whisker reaches it. Of the readings on the y axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `min`, which a sample of pairs does not have. - Derived - `y-max` — The largest reading in the sample, whether or not a whisker reaches it. Of the readings on the y axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `max`, which a sample of pairs does not have. - Derived - `re-median` — The middle reading — the rule across the box. Of the readings on the real axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `median`, which a sample of pairs does not have. - Derived - `re-q1` — The lower quartile: the median of the lower half of the ordered readings. Of the readings on the real axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `q1`, which a sample of pairs does not have. - Derived - `re-q3` — The upper quartile: the median of the upper half. Of the readings on the real axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `q3`, which a sample of pairs does not have. - Derived - `re-iqr` — The interquartile range, `q3 − q1` — the width of the box. Of the readings on the real axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `iqr`, which a sample of pairs does not have. - Derived - `re-min` — The smallest reading in the sample, whether or not a whisker reaches it. Of the readings on the real axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `min`, which a sample of pairs does not have. - Derived - `re-max` — The largest reading in the sample, whether or not a whisker reaches it. Of the readings on the real axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `max`, which a sample of pairs does not have. - Derived - `im-median` — The middle reading — the rule across the box. Of the readings on the imaginary axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `median`, which a sample of pairs does not have. - Derived - `im-q1` — The lower quartile: the median of the lower half of the ordered readings. Of the readings on the imaginary axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `q1`, which a sample of pairs does not have. - Derived - `im-q3` — The upper quartile: the median of the upper half. Of the readings on the imaginary axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `q3`, which a sample of pairs does not have. - Derived - `im-iqr` — The interquartile range, `q3 − q1` — the width of the box. Of the readings on the imaginary axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `iqr`, which a sample of pairs does not have. - Derived - `im-min` — The smallest reading in the sample, whether or not a whisker reaches it. Of the readings on the imaginary axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `min`, which a sample of pairs does not have. - Derived - `im-max` — The largest reading in the sample, whether or not a whisker reaches it. Of the readings on the imaginary axis: a box of a pair of samples publishes each axis's numbers under that axis's name, and no plain `max`, which a sample of pairs does not have. - Derived ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk boxplot(x: 2, 3, 3, 4, 4, 5, 5, 7, 12) ``` ```chalk boxplot(x: #trial.mass; colour: teal; label: control) ``` ```chalk boxplot(x: #trial.mass; whiskers: extremes) ``` ```chalk boxplot(x: #studies.change; y: #studies.impact) ``` --- # brace A curly brace spanning from one point to another, with its label at the tip: `brace(start: (0, 0); end: (4, 0); label: $w$)`. What `axis.brace` is for an interval ON an axis, this is for a distance anywhere in the picture — the height a projectile reaches, the object distance in a ray diagram, the side of a triangle. It bows to the LEFT of the direction it is written in, so swapping the ends (or `flip`) puts it on the other side, and its depth is in pixels, like an `angle`'s radius: notation on the picture rather than a measurement in the data. - Kind: Node - Section: Graph environment › Marks A brace spans two points with a curly brace and hangs its name at the tip — `brace(start: (0, 0); end: (4, 0); label: $R$)` — which is what an `axis.brace` is for an interval on an axis, for a distance anywhere in the picture. It bows to the left of the direction it is written in, so swapping its ends (or `flip`) moves it to the other side, and its `depth` is in pixels, like an angle's radius. ## Attributes - `start` — The point the span begins at, as a coordinate pair. Written first, and the brace bows to the left of the direction from here to `end`. - Required, Implicit, Animatable - Type: `parametised-coordinate-list` - Also written: `from` - Shorthand: `brace:value` - `end` — The point it ends at. - Required, Animatable - Type: `parametised-coordinate-list` - Also written: `to` - `label` — What the span is called, at the brace's tip — rich text, so `$maths$` renders. A brace with nothing to say is a bracket; the name is the reason to draw one. - Type: `rich-text` - `flip` — Bows to the other side of the line, without swapping the two ends round. - Animatable - Type: `boolean` - Default: `false` - `depth` — How far the brace bows out from the line it spans, in pixels — so it neither swells nor thins as the reader zooms. - Animatable - Type: `number` - Default: `10` - `colour` — The brace's colour, and its label's. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `neutral` - `width` — Stroke thickness. - Animatable - Type: `number` - Default: `1.5` - `opacity` — Opacity of the whole mark, 0 to 1. - Animatable - Type: `number` - Default: `1` - `size` — Label text size. - Animatable - Type: `number` - Also written: `text-size` - Default: `16` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and unlike a `cue`, a hidden element still frames the plot, so the axes do not jump when it appears. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. Nothing at all on a graph that is not editable. - Type: `boolean` - Also written: `locked` - Default: `false` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk brace(start: (0, 0); end: (4, 0); label: $w$) ``` ```chalk brace(start: (3, 0); end: (3, 2.4); label: $H$; colour: teal) ``` ```chalk brace(start: (1, 1); end: (4, 3); label: $d$; flip: true; depth: 14) ``` --- # circle A circle centred at (x, y), with `size` as its radius. One of seven shape marks that differ only in the outline drawn. - Kind: Node - Section: Graph environment › Marks - Family: Shapes - Only with xy, argand or polar plane ## Attributes On any plane: - `size` — Radius, in data units. - Animatable - Type: `parametised-number` - Default: `1` - `colour` — Colour. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `purple` - `fill` — Fill shape. - Animatable - Type: `boolean` - Default: `true` - `dashed` — Draws the outline dashed — the shape that is not there: a construction, a ghost of a previous position, a region ruled out. As `polygon`'s and `segment`'s. - Animatable - Type: `boolean` - Default: `false` - `opacity` — Opacity. - Animatable - Type: `number` - Default: `1` - `label` — Text drawn beside the element — rich text, so `$maths$` and inline formatting render. Fades and draws with the element. - Type: `rich-text` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and unlike a `cue`, a hidden element still frames the plot, so the axes do not jump when it appears. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. Nothing at all on a graph that is not editable. - Type: `boolean` - Also written: `locked` - Default: `false` - `text-size` — Text size. - Animatable - Type: `number` - Default: `20` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` Only with xy plane: - `x` — X coordinate. - Implicit, Animatable - Type: `parametised-number` - Shorthand: `circle:value` - `y` — Y coordinate. - Animatable - Type: `parametised-number` Only with argand plane: - `z` — On the argand plane, the position as one complex number — a complex expression: `3 + 2i`, `(3 + 2i)^2`, `sqrt(13) * e^(i * a)`, with `i` the unit and parameters read as complex. Instead of `re`/`im`. - Animatable - Type: `complex-expression` - `re` — On the argand plane, the real part of the position, a number; with `im`. `z:` gives both at once. - Animatable - Type: `parametised-number` - `im` — On the argand plane, the imaginary part of the position, a number; with `re`. `z:` gives both at once. - Animatable - Type: `parametised-number` Only with polar plane: - `r` — On the polar plane, the distance from the origin; with `theta`. - Animatable - Type: `parametised-number` - `theta` — On the polar plane, the angle anticlockwise from the right, in radians — `60deg` writes it in degrees; with `r`. On a categorical theta (`theta format:`), a name. - Animatable - Type: `parametised-number` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk circle(x: 0; y: 0; size: 2) ``` ```chalk circle(x: 2; y: 1; size: 1.5; fill: false; colour: purple) ``` --- # contour A two-variable expression drawn as its level sets. The expression is in x AND y (`(x - 2)^2 + (y - 3)^2`), grid-sampled over the visible window and traced where it takes equal values, so panning re-samples the surface and a parameter in the expression moves the level sets live. `y` is the vertical axis here, not a parameter: only the letters beyond x and y get footer sliders. - Kind: Node - Section: Graph environment › Marks - Only with xy, argand or polar plane A contour draws the level sets of a function of the plane, either `levels: 8` evenly spaced or `at: 1, 2, 4` exactly. Its expression may use `y` alongside `x`, which only a contour and a `heatmap` allow. It takes a `colour` that is a single hue or a ramp — `multicolour` (the full spectrum) or `temperature` (blue through white to red) — and exposes derived `min` and `max` over the visible window. A heatmap of the same expression paints the field its lines are drawn through. ```chalk canvas.graph{ heatmap(expression: sin(x) * cos(y); colour: temperature) contour(expression: sin(x) * cos(y); at: 0; colour: grey) } ``` ## Attributes - `expression` — Math expression for the surface's value at (x, y). Free letters beyond x and y are parameters. - Required, Implicit, Animatable - Type: `parametised-surface-expression` - Also written: `exp`, `f`, `func`, `function` - Shorthand: `contour:value` - `levels` — How many level sets to draw — resolved to round values of the expression, the way axis ticks are chosen. - Animatable - Type: `parametised-number` - Default: `12` - `at` — The exact level values, overriding `levels:`, for surfaces whose structure even spacing misrepresents — a quadratic bowl wants 1, 4, 9, 16. Entries are parametised: `at: k, 2 * k, 4 * k` rides a parameter slider. - Animatable - Type: `parametised-number-list` - Also written: `values`, `level-values` - `colour` — multicolour (the default) strokes each level along the full spectrum by its own value, violet low to red high; temperature is the blue → white → red ramp; any ordinary hue draws every ring alike. Both ramps are fixed colours, identical in light and dark mode. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black`, `multicolour`, `temperature` - Also written: `color` - Default: `multicolour` - `width` — Stroke width. - Animatable - Type: `number` - Default: `1.5` - `opacity` — How heavy the lines sit over the marks beneath them. - Animatable - Type: `number` - Default: `1` - `hidden` — Declared but not drawn — animatable, so `hidden: true !cue.to{false}(at: 2)` is its reveal. - Animatable - Type: `boolean` - Default: `false` - `lock` — A reader may not edit it on an editable graph. - Type: `boolean` - Also written: `locked` - Default: `false` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` ## Derived attributes Computed while the element renders — read one with `#id.name` in attribute position or `{{#id.name}}` in prose, never set. - `min` — The smallest value the surface takes over the sampled window — for a loss surface, the number the whole picture is about. Cite it with `{{#id.min}}`. - Derived - `max` — The largest value the surface takes over the sampled window. - Derived ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk contour(expression: (x - 2)^2 + (y - 3)^2; levels: 8) ``` ```chalk contour(expression: x^2 - y^2; at: -4, -1, 0, 1, 4; colour: teal) ``` ```chalk contour(expression: (x - a)^2 + (y - b)^2; levels: 10; opacity: 0.7) ``` ```chalk contour(expression: x^2 + y^2; at: k, 2 * k, 4 * k, 8 * k) ``` ```chalk contour(expression: sin(x) * cos(y); levels: 14; colour: temperature) ``` --- # curve An expression in x plotted as a curve. `expression` is the implicit attribute; `domain` and `range` clip where it draws. Publishes derived `max` and `min` over the plotted domain. - Kind: Node - Section: Graph environment › Marks - Only with xy, argand or polar plane A curve plots an expression in `x`: `curve(expression: x^2; colour: blue)` — in `theta` on the polar plane, where it is `r(θ)`. Expressions may reference parameters — `a * sin(b * x)` bends live as the reader drags `a` and `b` — and `domain` / `range` clip where it draws. A curve exposes derived `max` and `min` over its plotted domain. A function defined in pieces is written with `min(a, b)`, `max(a, b)` and `clamp(x, lo, hi)` rather than as several curves with clipped domains, and it stays one curve under a parameter: a response that saturates is `min(2x, 10)`, and Huber's `ρ` — quadratic in the middle, linear in the tails — is the clamped distance `clamp(abs(x), 0, c)^2/2 + c * (abs(x) - clamp(abs(x), 0, c))`. (Not `min(x^2/2, c * abs(x) - c^2/2)`: the two differ by `(abs(x) - c)^2/2`, which is never negative, so that `min` is the linear branch everywhere.) Or a curve is a position in `t`: `curve(x: cos(t); y: sin(t); t: [0, 2pi])` on the ordinary plane, `z(t)` on the argand one, `r` and `theta` in `t` on the polar one — with the interval `t` runs over, and no `expression`. A parametric curve takes `fill`, since its path can enclose something: a closed loop fills itself, and an arc closes across its own chord — `close` names a point to come back through first, so an arc closed through the centre of its circle is the sector. A y(x) curve is refused both: the region under a function is an `integral`, and saying so keeps one idea in one place. ## Attributes On any plane: - `t` — The interval the parametric variable runs over — `[0, 2pi]`, ends parametised. Required with the parametric coordinates; not with `expression`. - Animatable - Type: `parametised-interval` - `colour` — Curve colour. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `blue` - `width` — Stroke width. - Animatable - Type: `number` - Default: `2` - `dashed` — Dashed stroke. - Animatable - Type: `boolean` - Default: `false` - `fill` — Shades the region the path encloses — the PARAMETRIC form only, where a path can enclose one: an ellipse, a cardioid, a lissajous loop. An arc closes straight across, which shades the segment of a circle; `close` turns that into the sector. A y(x) curve takes `integral` instead, which shades the area under a function and reports what it is worth. - Animatable - Type: `boolean` - Also written: `filled` - Default: `false` - `close` — A point the shaded region comes back through before it closes, as a coordinate pair. An arc closed through the centre of its circle is a sector — the wedge no other mark can draw, since `polygon` has straight sides and `integral` closes to the axis. Nothing without `fill`. - Animatable - Type: `parametised-coordinate-list` - Also written: `close-at` - `domain` — Restrict domain. - Animatable - Type: `parametised-interval` - Also written: `x-domain` - `range` — Restrict range. - Animatable - Type: `parametised-interval` - Also written: `y-range` - `endpoints` — Endpoint decoration: an arrow where the curve runs on past the frame, and a dot where its domain stops it — filled for a closed end, open for an open one. Holes in the function are marked either way. - Animatable - Type: `boolean` - Also written: `arrowheads`, `arrow`, `arrowhead`, `arrows` - Default: `true` - `label` — Text drawn beside the element — rich text, so `$maths$` and inline formatting render. Fades and draws with the element. - Type: `rich-text` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and unlike a `cue`, a hidden element still frames the plot, so the axes do not jump when it appears. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. Nothing at all on a graph that is not editable. - Type: `boolean` - Also written: `locked` - Default: `false` - `text-size` — Text size. - Animatable - Type: `number` - Default: `20` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` Only with xy plane: - `expression` — The curve as a function of the plane's independent axis: y(x) on the ordinary plane, r(theta) on the polar one. Refused on the argand plane, which has no independent axis — write a position in t there. Not with the parametric coordinates. - Implicit, Animatable - Type: `curve-expression` - Also written: `exp`, `f`, `func`, `function` - Shorthand: `curve:value` - `x` — A position in t: the x coordinate as an expression in `t`, with `y:` and `t:` — `curve(x: cos(t); y: sin(t); t: [0, 2pi])`. On the ordinary plane; not with `expression`. - Animatable - Type: `parametric-expression` - `y` — A position in t: the y coordinate as an expression in `t`, with `x:` and `t:`. - Animatable - Type: `parametric-expression` Only with argand plane: - `z` — On the argand plane, the curve as z(t): a complex expression in `t` — `curve(z: sqrt(13) * e^(i * t); t: [0, 2pi])` — with `t:`. - Animatable - Type: `complex-parametric-expression` Only with polar plane: - `expression` — The curve as a function of the plane's independent axis: y(x) on the ordinary plane, r(theta) on the polar one. Refused on the argand plane, which has no independent axis — write a position in t there. Not with the parametric coordinates. - Implicit, Animatable - Type: `curve-expression` - Also written: `exp`, `f`, `func`, `function` - Shorthand: `curve:value` - `r` — On the polar plane, a position in t: the distance as an expression in `t`, with `theta:` and `t:`. - Animatable - Type: `parametric-expression` - `theta` — On the polar plane, a position in t: the angle as an expression in `t`, with `r:` and `t:`. - Animatable - Type: `parametric-expression` ## Derived attributes Computed while the element renders — read one with `#id.name` in attribute position or `{{#id.name}}` in prose, never set. - `max` — The largest value the expression takes over the curve's effective domain, tracking parameters live. - Derived - `min` — The smallest value the expression takes over the curve's effective domain, tracking parameters live. - Derived ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk curve(expression: x^2; colour: blue) ``` ```chalk curve(expression: sin(x); domain: [-3.14, 3.14]) ``` ## Notes - Give a curve a node id — `curve#flight(f: …)` — to let `cue.trace` follow it with `path: #flight`. --- # density A sample drawn as the curve of its density: a kernel density estimate worked out from the readings themselves, so the curve is one line of chalk rather than a column of heights computed elsewhere. Which coordinate carries the readings is the axis they are measured on, and the estimate rises up the other — `x:` spreads them along x with the curve standing over it, `y:` turns the picture a quarter turn. Two coordinates are two samples with no axis left for the estimate to rise up, and are refused. Drawn only across the range the readings cover, and trimmed square at each end for the reason a `violin` is: a density claimed past the largest reading is a picture of measurements nobody took. The area under it is 1. `bandwidth:` is parametised, which is most of what this mark is for — the same readings under a bandwidth the reader is dragging, bumps appearing and flattening as they go. - Kind: Node - Section: Graph environment › Marks - Only with xy plane A density is the estimate a `violin` draws, as one curve rather than a mirrored shape — a violin unrolled, standing on the axis its readings are measured on. One coordinate, and which one it is says which axis that is: `density(x: #sample.height)` spreads the readings along x with the curve rising up y, and `y:` turns the picture a quarter turn. Two coordinates leave no axis for the estimate to rise up and are refused. It is trimmed to the readings for the same reason a violin is, the area under it is 1, and `bandwidth` means what it means there — so the document this mark exists for is one sample, three bandwidths and a slider. It publishes `mode`, `peak` and the `bandwidth` actually used, which under Silverman's rule is a number prose otherwise has no way to state. ```chalk canvas.graph:xy{ density#kde(x: #sample.income; bandwidth: h; label: $h$ = {{round(#kde.current-bandwidth, 2)}}) parameter(var: h; range: [0.2, 4]; default: 1; name: bandwidth) }( x axis label: income ($) ) ``` ## Attributes - `x` — The readings — numbers written out, or a dataset column bound whole. The sample itself, not a curve worked out by hand; writing them on x spreads the density along x, with the estimate rising up y. - Implicit, Animatable - Type: `series` - Shorthand: `density:value` - `y` — The readings when the sample is measured up the y axis — the same picture, turned a quarter turn, with the estimate running out along x. Not with `x:`. - Animatable - Type: `series` - `bandwidth` — The width of the kernel each reading is smoothed over, in the readings' own units. Left out, Silverman's rule of thumb, 0.9 × min(σ, IQR ÷ 1.34) × n^(−1/5). Smaller shows every bump in the sample and larger smooths them away; parametised, so a slider drives it. A written number must be positive. - Animatable - Type: `parametised-number` - `colour` — Colour. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `purple` - `width` — Stroke width, as a `curve`'s is. - Animatable - Type: `number` - Default: `2` - `fill` — Wash the area under the curve in its own colour. Off by default: a family of densities at three bandwidths reads as three lines, and three washes over one another read as nothing. - Animatable - Type: `boolean` - Default: `false` - `opacity` — Opacity. - Animatable - Type: `number` - Default: `1` - `label` — Text drawn beside the element — rich text, so `$maths$` and inline formatting render. Placed at the curve's peak, which is where a family of densities is told apart. - Type: `rich-text` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and unlike a `cue`, a hidden element still frames the plot, so nothing moves when it appears. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. Nothing at all on a graph that is not editable. - Type: `boolean` - Also written: `locked` - Default: `false` - `text-size` — Text size. - Animatable - Type: `number` - Default: `20` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). - Animatable - Type: `parametised-number` ## Derived attributes Computed while the element renders — read one with `#id.name` in attribute position or `{{#id.name}}` in prose, never set. - `mode` — The reading the curve peaks at — the sample's mode as the estimate sees it, which moves with the bandwidth. - Derived - `peak` — The density at that reading — how tall the curve is at its highest. - Derived - `current-bandwidth` — The width actually smoothed by: the one written, or the one Silverman's rule chose — which is the number nobody knows without being told it. Named apart from the `bandwidth` attribute, which says what was asked for rather than what was used. - Derived ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk density(x: #sample.height) ``` ```chalk density(x: #sample.height; bandwidth: h; colour: purple) parameter(var: h; range: [0.2, 4]; default: 1; name: bandwidth) ``` ```chalk density(x: #avp.income; bandwidth: 0.5; label: narrow) density(x: #avp.income; bandwidth: 2; label: wide; colour: teal) ``` --- # diamond A diamond centred at (x, y), scaled by `size`. One of seven shape marks that differ only in the outline drawn. - Kind: Node - Section: Graph environment › Marks - Family: Shapes - Only with xy, argand or polar plane ## Attributes On any plane: - `size` — Distance from the centre to each of the four vertices — half the diagonal. - Animatable - Type: `parametised-number` - Default: `1` - `colour` — Colour. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `purple` - `fill` — Fill shape. - Animatable - Type: `boolean` - Default: `true` - `dashed` — Draws the outline dashed — the shape that is not there: a construction, a ghost of a previous position, a region ruled out. As `polygon`'s and `segment`'s. - Animatable - Type: `boolean` - Default: `false` - `opacity` — Opacity. - Animatable - Type: `number` - Default: `1` - `label` — Text drawn beside the element — rich text, so `$maths$` and inline formatting render. Fades and draws with the element. - Type: `rich-text` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and unlike a `cue`, a hidden element still frames the plot, so the axes do not jump when it appears. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. Nothing at all on a graph that is not editable. - Type: `boolean` - Also written: `locked` - Default: `false` - `text-size` — Text size. - Animatable - Type: `number` - Default: `20` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` Only with xy plane: - `x` — X coordinate. - Implicit, Animatable - Type: `parametised-number` - Shorthand: `diamond:value` - `y` — Y coordinate. - Animatable - Type: `parametised-number` Only with argand plane: - `z` — On the argand plane, the position as one complex number — a complex expression: `3 + 2i`, `(3 + 2i)^2`, `sqrt(13) * e^(i * a)`, with `i` the unit and parameters read as complex. Instead of `re`/`im`. - Animatable - Type: `complex-expression` - `re` — On the argand plane, the real part of the position, a number; with `im`. `z:` gives both at once. - Animatable - Type: `parametised-number` - `im` — On the argand plane, the imaginary part of the position, a number; with `re`. `z:` gives both at once. - Animatable - Type: `parametised-number` Only with polar plane: - `r` — On the polar plane, the distance from the origin; with `theta`. - Animatable - Type: `parametised-number` - `theta` — On the polar plane, the angle anticlockwise from the right, in radians — `60deg` writes it in degrees; with `r`. On a categorical theta (`theta format:`), a name. - Animatable - Type: `parametised-number` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk diamond(x: 0; y: 0; size: 2) ``` ```chalk diamond(x: -1; y: -1; size: 1.2; opacity: 0.8) ``` --- # fit A least-squares fit through the observations of the scatter named in `of:`, and no others — a graph may carry two fits over two scatters. `model` chooses the family and `degree` the polynomial degree; giving `degree` a parameter rather than a number puts the degree under the reader's control through that parameter's slider. Publishes derived `slope`, `intercept` and `r2`. - Kind: Node - Section: Graph environment › Marks - Only with xy plane - Writing with it: Reading computed values in prose A fit is the least-squares line or polynomial through one scatter's observations, named in `of:` — its head slot, so `fit: #observations` is the same as `fit(of: #observations)`. Give `degree` a parameter instead of a number and the reader drives it — `Interaction` shows that pairing. `residuals` draws the differences from a fit to the points it was found from. ```chalk canvas.graph#fit-graph{ scatter#observations(x: 1, 2, 3, 4; y: 2.0, 2.4, 3.1, 4.8; colour: grey) fit(of: #observations; model: polynomial; degree: 1; colour: teal) }( x-axis-label: x y-axis-label: y ) ``` A fit fits the mark it names and nothing else, so a graph can carry two of them: draw the sample twice, once whole and once without the point in question, and the two lines stand beside each other with their own `slope` and `r2`. To fit part of a sample, draw that part as its own scatter and fit that. ```chalk canvas.graph{ scatter#all(x: 1, 2, 3, 4, 5; y: 2.1, 3.9, 6.2, 7.8, 14.0; colour: grey) scatter#typical(x: 1, 2, 3, 4; y: 2.1, 3.9, 6.2, 7.8; colour: blue) fit#with-outlier(of: #all; degree: 1; colour: grey) fit#without(of: #typical; degree: 1; colour: blue) } ``` > A fit exposes derived `slope`, `intercept` and `r2`. The slope is the fitted polynomial's derivative at zero — exact, and live under whatever degree is in force — so prose citing `{{#f.slope}}` cannot disagree with the graph. ## Attributes - `of` — The scatter to fit, by its id — `fit(of: #readings)`. The fit is taken through that mark's observations alone, so two scatters in one graph carry two independent fits. To fit part of a sample, draw that part as its own scatter and name it here. - Required, Implicit - Type: `reference` - Also written: `through`, `data` - Shorthand: `fit:value` - `model` — Model family: polynomial or linear. - Options: `polynomial`, `linear` - Default: `polynomial` - `degree` — Polynomial degree — a number, or an expression of parameters (degree: d) when the reader should drive it through a parameter slider. - Animatable - Type: `parametised-number` - Default: `1` - `colour` — Curve colour. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `teal` - `width` — Stroke width. - Animatable - Type: `number` - Default: `2` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and unlike a `cue`, a hidden element still frames the plot, so the axes do not jump when it appears. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. Nothing at all on a graph that is not editable. - Type: `boolean` - Also written: `locked` - Default: `false` ## Derived attributes Computed while the element renders — read one with `#id.name` in attribute position or `{{#id.name}}` in prose, never set. - `slope` — The x¹ coefficient of the current fit — live under the reader-draggable degree. - Derived - `intercept` — The constant term of the current fit. - Derived - `r2` — The coefficient of determination of the current fit against the observations it was taken through. - Derived ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk fit(of: #readings; model: polynomial; degree: d; colour: teal) ``` ```chalk scatter#all(x: #trial.dose; y: #trial.response) scatter#typical(x: #clean.dose; y: #clean.response) fit#with-outlier(of: #all; degree: 1; colour: grey) fit#without(of: #typical; degree: 1; colour: blue) ``` --- # heatmap The same surface a contour traces, shaded instead: one expression in x AND y, grid-sampled over the visible window and drawn as a continuous wash of the mark's hue — bare where the value is low, full where it is high. The two marks compose: a heatmap under a contour shades the field and traces its level sets over it, and `@let` keeps the shared expression written once. As with contour, `y` is the vertical axis, not a parameter. - Kind: Node - Section: Graph environment › Marks - Only with xy, argand or polar plane A heatmap paints a function of the plane. Its expression may use `y` alongside `x`, which only a heatmap and a `contour` allow. It takes a `colour` that is a single hue or a ramp — `multicolour` (the full spectrum) or `temperature` (blue through white to red) — and exposes derived `min` and `max` over the visible window. A contour of the same expression draws its level sets over it. ```chalk canvas.graph{ heatmap(expression: sin(x) * cos(y); colour: temperature) contour(expression: sin(x) * cos(y); at: 0; colour: grey) } ``` ## Attributes - `expression` — Math expression for the surface's value at (x, y). Free letters beyond x and y are parameters. - Required, Implicit, Animatable - Type: `parametised-surface-expression` - Also written: `exp`, `f`, `func`, `function` - Shorthand: `heatmap:value` - `range` — The value window the ramp spans, e.g. `[0, 40]`; values outside clamp. Absent, the ramp spans the sampled extrema — which re-normalises as a parameter drags; an authored range holds still, exactly as an authored domain does. - Animatable - Type: `parametised-interval` - `invert` — Flip the ramp, putting deep colour at low values rather than high. - Animatable - Type: `boolean` - Default: `false` - `colour` — multicolour (the default) runs the full spectrum, violet low to red high; temperature is the blue → white → red wash — pair it with a symmetric `range:` to pin white at zero; any ordinary hue shades its own intensity ramp instead. Both ramps are fixed colours, identical in light and dark mode. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black`, `multicolour`, `temperature` - Also written: `color` - Default: `multicolour` - `opacity` — How heavy the wash sits over the plot. - Animatable - Type: `number` - Default: `0.75` - `hidden` — Declared but not drawn — animatable, so `hidden: true !cue.to{false}(at: 2)` is its reveal. - Animatable - Type: `boolean` - Default: `false` - `lock` — A reader may not edit it on an editable graph. - Type: `boolean` - Also written: `locked` - Default: `false` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` ## Derived attributes Computed while the element renders — read one with `#id.name` in attribute position or `{{#id.name}}` in prose, never set. - `min` — The smallest value the surface takes over the sampled window. Cite it with `{{#id.min}}`. - Derived - `max` — The largest value the surface takes over the sampled window. - Derived ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk heatmap(expression: x*y; range: [-9, 9]; colour: temperature) ``` ```chalk heatmap(expression: x^2 + y^2) ``` ```chalk heatmap(expression: (x - 2)^2 + (y - 3)^2; colour: red; invert: true) ``` ```chalk heatmap(expression: {{sse}}; opacity: 0.5) contour(expression: {{sse}}; at: 1, 2, 4, 8, 16) ``` --- # hexagon A hexagon centred at (x, y), scaled by `size`. One of seven shape marks that differ only in the outline drawn. - Kind: Node - Section: Graph environment › Marks - Family: Shapes - Only with xy, argand or polar plane ## Attributes On any plane: - `size` — Distance from the centre to each vertex, the hexagon standing point-up. - Animatable - Type: `parametised-number` - Default: `1` - `colour` — Colour. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `purple` - `fill` — Fill shape. - Animatable - Type: `boolean` - Default: `true` - `dashed` — Draws the outline dashed — the shape that is not there: a construction, a ghost of a previous position, a region ruled out. As `polygon`'s and `segment`'s. - Animatable - Type: `boolean` - Default: `false` - `opacity` — Opacity. - Animatable - Type: `number` - Default: `1` - `label` — Text drawn beside the element — rich text, so `$maths$` and inline formatting render. Fades and draws with the element. - Type: `rich-text` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and unlike a `cue`, a hidden element still frames the plot, so the axes do not jump when it appears. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. Nothing at all on a graph that is not editable. - Type: `boolean` - Also written: `locked` - Default: `false` - `text-size` — Text size. - Animatable - Type: `number` - Default: `20` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` Only with xy plane: - `x` — X coordinate. - Implicit, Animatable - Type: `parametised-number` - Shorthand: `hexagon:value` - `y` — Y coordinate. - Animatable - Type: `parametised-number` Only with argand plane: - `z` — On the argand plane, the position as one complex number — a complex expression: `3 + 2i`, `(3 + 2i)^2`, `sqrt(13) * e^(i * a)`, with `i` the unit and parameters read as complex. Instead of `re`/`im`. - Animatable - Type: `complex-expression` - `re` — On the argand plane, the real part of the position, a number; with `im`. `z:` gives both at once. - Animatable - Type: `parametised-number` - `im` — On the argand plane, the imaginary part of the position, a number; with `re`. `z:` gives both at once. - Animatable - Type: `parametised-number` Only with polar plane: - `r` — On the polar plane, the distance from the origin; with `theta`. - Animatable - Type: `parametised-number` - `theta` — On the polar plane, the angle anticlockwise from the right, in radians — `60deg` writes it in degrees; with `r`. On a categorical theta (`theta format:`), a name. - Animatable - Type: `parametised-number` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk hexagon(x: 0; y: 0; size: 2) ``` ```chalk hexagon(x: 2; y: -1; size: 1.4; fill: false) ``` --- # histogram A sample drawn as bars over the values it takes. Written two ways: the READINGS, which the mark bins itself — `histogram(x: #sample.height; bins: 12)`, where `bins` is how many — or the counts of an already-tallied sample in `values`, with `bins` the edges they fall between (one more number than there are counts). Which coordinate carries the readings is the axis the bins land on, and so which way the bars grow; `orientation` says the same thing for the counts form. Bars meet at their shared edges, take a colour each, and can print their value. - Kind: Node - Section: Graph environment › Marks - Only with xy or polar plane A histogram is a rectangle mark and a different thing from a `bar`: its bins are edges, so its width is data, and it draws on a numeric axis. It takes the readings themselves — `histogram(x: #sample.height; bins: 12)` — and bins them, `bins` saying how many; parametised (`bins: k`) the reader re-bins the sample for themselves, which is the quickest way to see how much of the shape was the choice of grid. The coordinate the readings are written on is the axis the bins land on and so which way the bars grow (`x` up from the x axis, `y` out from the y axis, `theta` a wind rose on the polar plane, its bins edges on `theta`). A sample already tallied is written the other way instead — `values` the counts and `bins` the edges they fall between, one more number than there are counts, with `orientation` for the direction no coordinate is there to say. ## Attributes On any plane: - `values` — The counts, one per bin — a sample the author has already tallied. `bins` is then the edges they fall between, one more number than there are counts. Never beside readings: a histogram is given the counts or works them out, not both. e.g. 5, 12, 8 - Implicit, Animatable - Type: `number-list` - Shorthand: `histogram:value` - `bins` — How many bins, or where their edges fall. A single number is a count — `bins: 12`, or parametised, `bins: k`, so a slider re-bins the sample — and it needs readings to bin. Two or more numbers are the edges themselves, in order, one more than there are bars, and may be unequal (`0, 10, 20, 30`). Computed bins span the sample exactly, smallest reading to largest; with no `bins` at all the count is the square root of the sample size. - Animatable - Type: `histogram-bins` - `colours` — Colour for each bar, cycled in order — one colour colours the lot. If fewer colours than bars are given, the list starts again from the first. - Animatable - Type: `colour-list` - Also written: `colors`, `colour`, `color` - Default: `blue` - `orientation` — Bar direction for the counts form. 'vertical' grows upward from the x-axis; 'horizontal' grows rightward from the y-axis. Readings name their own axis — `x` or `y` — and that is the direction, so this says nothing beside them. - Options: `vertical`, `horizontal` - Default: `vertical` - `show-labels` — When true, renders the numeric value above each bar (or to its right for horizontal orientation). - Animatable - Type: `boolean` - Default: `false` - `label` — Text drawn beside the element — rich text, so `$maths$` and inline formatting render. Fades and draws with the element. - Type: `rich-text` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and unlike a `cue`, a hidden element still frames the plot, so the axes do not jump when it appears. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. Nothing at all on a graph that is not editable. - Type: `boolean` - Also written: `locked` - Default: `false` - `text-size` — Text size. - Animatable - Type: `number` - Default: `20` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` Only with xy plane: - `x` — The readings on the x axis — the sample itself, taken as a `scatter` takes one: numbers, or a dataset column (`#sample.height`). The mark bins them; `bins` says how many. Written on `x`, the grid lies along x and the bars stand up from it. - Animatable - Type: `series` - `y` — The same readings measured up the y axis instead: the grid lies up y and the bars run out rightward from it. The readings' own coordinate is the orientation, so `orientation` is not written beside it. - Animatable - Type: `series` Only with polar plane: - `theta` — The readings as angles, in radians, on the polar plane: the turn is cut into `bins` sectors and each count reaches out from the centre — a rose of the sample rather than of counts written by hand. - Animatable - Type: `series` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk histogram( x: #sample.height bins: 12 colour: teal ) ``` ```chalk histogram( x: #sample.height bins: k ) ``` ```chalk histogram( y: 2.1, 2.4, 3.8, 4.0, 4.1, 5.6 bins: 4 ) ``` ```chalk histogram( bins: 0, 10, 20, 30, 40 values: 5, 12, 8, 3 colour: teal ) ``` ```chalk histogram( bins: 0, 5, 10, 15 values: 3, 7, 2 orientation: horizontal colour: purple ) ``` --- # integral The area under a curve between two values, shaded. `of:` takes `#bell` for a curve in the same environment or an expression to shade directly, and the region is sampled from the curve itself, so it follows an animated expression and a parameter driving either limit. The region closes along the axis: what is shaded is the area between the curve and y = 0, and where the curve dips below the axis the shading follows it down. - Kind: Node - Section: Graph environment › Marks - Only with xy plane - Writing with it: Reading computed values in prose An integral shades the area under a referenced curve between two bounds — `integral(of: #bell; from: -1; to: 1)` — and exposes the computed area as derived `value`. Its bounds may be expressions, so a parameter can sweep the region while the reader watches the value change. `base` says what the area closes down to: the axis, unless it names another curve (`base: #demand`) or an expression of its own — `base: 6` shades down to that height. The area between two curves is not a mark of its own because it is not an idea of its own, and the reported `value` follows the floor. A region a parametric `curve` encloses is that curve's `fill` instead. ## Attributes - `of` — The curve to shade under: `#id` for a curve in this environment, or an expression shaded directly. - Required, Implicit - Type: `curve-path` - Also written: `curve`, `under` - Shorthand: `integral:value` - `from` — Where the region starts. May be an expression in the environment's parameters. - Required, Animatable - Type: `parametised-number` - Also written: `a`, `start` - `to` — Where it ends. May be an expression in the environment's parameters. - Required, Animatable - Type: `parametised-number` - Also written: `b`, `end` - `base` — What the region closes DOWN to: the axis unless this names something else — another curve in the environment (`base: #demand`) or an expression of its own, and a number is one (`base: 6` shades down to that height). The area between two curves is not a special mark, because it is not a special idea: an integral is the region between a function and a floor, and y = 0 is only the floor nothing else was named. The reported `value` follows, so with a base it is the area BETWEEN. - Type: `curve-path` - Also written: `down-to`, `floor` - `colour` — The wash and its edges. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `blue` - `opacity` — How heavy the wash is. - Animatable - Type: `number` - Default: `0.35` - `edges` — Draw a line down to the axis at each limit. - Type: `boolean` - Default: `true` - `label` — Text drawn in the middle of the region — rich text, so `$maths$` renders. - Type: `rich-text` - `hidden` — Declared and framed, but not drawn. - Animatable - Type: `boolean` - Default: `false` - `lock` — A reader may not edit it on an editable graph. - Type: `boolean` - Also written: `locked` - Default: `false` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` ## Derived attributes Computed while the element renders — read one with `#id.name` in attribute position or `{{#id.name}}` in prose, never set. - `value` — The computed area of the shaded interval — the number the shading is. Cite it with `{{#id.value}}` and a probability label can never drift from the drawing. - Derived ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk integral(of: #bell; from: -1; to: 1; label: $0.68$) ``` ```chalk integral(of: x^2; from: 0; to: b; colour: teal) ``` ```chalk integral(of: #velocity; a: 0; b: 5; edges: false; opacity: 0.2) ``` --- # line A path through a run of points. `x:` and `y:` hold one axis each, paired by position — numbers written out, or dataset columns (`x: #co2.year; y: #co2.ppm`). Drawn straight between the points by default; `interpolate: step` instead holds each value until the next x and then jumps, giving a staircase, and marks each jump with a dot: filled where the function lands, hollow where it left. - Kind: Node - Section: Graph environment › Marks - Only with xy, argand or polar plane A line reads `x` / `y` the way a `scatter` does, and takes `interpolate`: `linear` joins the readings, `bezier` rounds the corners off with a cubic through every one of them, and `step` holds each value until the next x and then jumps. A rounded line claims the quantity varied smoothly in between, so it belongs on a trajectory and not on a tally. `polygon` takes `linear` and `bezier` the same way. ## Attributes On any plane: - `period` — Long-format data: one row per x per period, this naming the period column. The line then shows the slice at whatever the environment's parameter over that column holds, gliding between the two periods that bracket it. - Type: `reference` - Also written: `at` - `colour` — Colour of the path. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `pink` - `width` — Stroke width. - Animatable - Type: `parametised-number` - Default: `2` - `interpolate` — How it gets from one reading to the next: `linear` joins them, `step` holds and jumps, `bezier` rounds the corners off with a cubic through every reading whose handles sit at the midpoints of the legs either side (t = 0.5, so it cannot overshoot). A curve says the quantity varied smoothly between the readings; a tally did not. - Animatable - Options: `linear`, `step`, `bezier` - Also written: `interpolation` - Default: `linear` - `endpoints` — On a step, the dots at each jump — filled where the value is taken, hollow where it is not. - Type: `boolean` - Also written: `dots` - Default: `true` - `risers` — On a step, the vertical joins between treads. Off by default: a function has one value at each x. - Type: `boolean` - Also written: `connect` - Default: `false` - `label` — Text drawn beside the line — rich text. - Type: `rich-text` - `hidden` — Declared and framed, but not drawn. - Animatable - Type: `boolean` - Default: `false` - `lock` — A reader may not edit it on an editable graph. - Type: `boolean` - Also written: `locked` - Default: `false` - `text-size` — Size of the label. - Animatable - Type: `number` - Default: `20` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` Only with xy plane: - `x` — The x readings: numbers (`0, 1, 2` — entries may be expressions of parameters), or a dataset column (`#co2.year`). A column of names reads against a categorical axis (`x format:`, or the axis’s `auto` rule); an ISO date column reads as fractional years (`2024-03-15` → ≈2024.2), and `auto` labels them in years. Paired with `y` by position. - Implicit, Animatable - Type: `series` - Shorthand: `line:value` - `y` — The y readings, paired with `x` by position. - Animatable - Type: `series` Only with argand plane: - `z` — On the argand plane, the readings as complex numbers: written out (`1 + i, 2 - i, -3i`, a bare number a real), or a column of complex literals bound whole (`#roots.z`). Instead of `re`/`im`. - Animatable - Type: `complex-series` - `re` — On the argand plane, the readings' real parts, one per reading — numbers written out or a column — paired with `im` by position. - Animatable - Type: `series` - `im` — On the argand plane, the readings' imaginary parts, paired with `re` by position. - Animatable - Type: `series` Only with polar plane: - `r` — On the polar plane, the readings' distances from the origin, paired with `theta` by position. - Animatable - Type: `series` - `theta` — On the polar plane, the readings' angles in radians (`60deg` for degrees), paired with `r` by position; names on a categorical theta. - Animatable - Type: `series` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk line(x: 0, 2, 4; y: 0, 2, 1; colour: pink) ``` ```chalk line(x: 0, 1, 2; y: 0, 0.5, 1; interpolate: step) ``` ```chalk line(x: #co2.year; y: #co2.ppm) ``` ```chalk line(x: #temperatures.month; y: #temperatures.sydney) ``` --- # lollipop A value drawn as a stem with a dot at its end. What a `bar` is for a quantity that fills an interval, this is for one that stands at a POINT — a discrete distribution above all, where `P(X = k)` belongs to the single value k and a bar drawn there claims a width the quantity does not have. Two forms, as a `histogram` has: a position and a value, paired by position as a `scatter`'s are (`lollipop(x: 0, 1, 2; y: 0.25, 0.5, 0.25)`), or ONE axis alone, which is the readings — `lollipop(x: #sample.score)` stands a stem of height 3 at every value that occurs three times, a tally by distinct value that invents no grid at all. Which axis carries the readings is the axis the stems stand on; in the paired form a categorical axis decides (nothing is measured along names), and where both axes are numeric `orientation` does. The stems stand on the baseline, so the value axis reaches it. - Kind: Node - Section: Graph environment › Marks - Only with xy plane A lollipop is for a quantity that stands at a *point* rather than filling an interval: a stem from the baseline with a dot at its end. `lollipop(x: 0, 1, 2; y: 0.25, 0.5, 0.25)` is a mass function written out, and it is the honest picture of one — `P(X = k)` belongs to the single value `k`, and a bar drawn there claims a width the quantity has not got. It takes the two forms a `histogram` does: a position and a value paired by position, or one axis alone, which is the readings — `lollipop(x: #rolls.face)` stands a stem of height three at every value that occurs three times, a tally by distinct value that invents no grid at all, which is what discrete data deserves instead of bins. A categorical axis is where the stems stand (names have no height), and with two numeric axes `orientation` says which way they run. ## Attributes - `x` — Where the stems stand: numbers, names, or a dataset column (`#rolls.face`). Paired with `y` by position; a pair either axis cannot read is dropped whole. Written alone it is the readings instead, and the mark counts them. - Implicit, Animatable - Type: `series` - Shorthand: `lollipop:value` - `y` — The values the stems reach, paired with `x` by position — or, written alone, the readings to be counted, with the counts running out along x. - Animatable - Type: `series` - `orientation` — Which way the stems run when nothing else says: 'vertical' up from the x axis, 'horizontal' out from the y axis. A categorical axis says it first, and in the readings form the readings' own axis does, so this is read only when two numeric axes leave the question open. - Options: `vertical`, `horizontal` - Default: `vertical` - `colours` — Colour for each stem and its dot, cycled in order — one colour colours the lot. If fewer colours than stems are given, the list starts again from the first. - Animatable - Type: `colour-list` - Also written: `colors`, `colour`, `color` - Default: `blue` - `size` — The dot at the stem's end, in the same units a `scatter`'s dots take. - Animatable - Type: `parametised-number` - Default: `6` - `width` — The stem's thickness, as a `line`'s. - Animatable - Type: `parametised-number` - Default: `2` - `opacity` — Opacity of the whole mark, 0 to 1. - Animatable - Type: `number` - Default: `1` - `show-labels` — When true, writes each stem's value beside its dot — clear of it, above for vertical stems and beyond the dot for horizontal ones. Animatable, so a step can fade the numbers in over a picture the reader has already read. - Animatable - Type: `boolean` - Also written: `labels` - Default: `false` - `label` — Text drawn beside the element — rich text, so `$maths$` and inline formatting render. Fades and draws with the element. - Type: `rich-text` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and unlike a `cue`, a hidden element still frames the plot, so the axes do not jump when it appears. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. Nothing at all on a graph that is not editable. - Type: `boolean` - Also written: `locked` - Default: `false` - `text-size` — Text size. - Animatable - Type: `number` - Default: `20` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk lollipop( x: 0, 1, 2, 3 y: 0.125, 0.375, 0.375, 0.125 ) ``` ```chalk lollipop(x: #rolls.face) ``` ```chalk lollipop( x: 0.42, 0.31, 0.19 y: North, South, East show-labels: true ) ``` --- # point A single point at (x, y). `x` is the implicit attribute, and `shape:` chooses the marker drawn. For many points at once, scatter takes one coordinate list. - Kind: Node - Section: Graph environment › Marks ## Attributes On any plane: - `size` — Point size. - Animatable - Type: `parametised-number` - Default: `6` - `colour` — Colour. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `orange` - `shape` — The mark the point is drawn as. A dot unless asked otherwise. - Options: `circle`, `square`, `rectangle`, `triangle`, `star`, `diamond`, `hexagon`, `cross` - Default: `circle` - `opacity` — Opacity. - Animatable - Type: `number` - Default: `1` - `label` — Text drawn beside the element — rich text, so `$maths$` and inline formatting render. Fades and draws with the element. - Type: `rich-text` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and unlike a `cue`, a hidden element still frames the plot, so the axes do not jump when it appears. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. Nothing at all on a graph that is not editable. - Type: `boolean` - Also written: `locked` - Default: `false` - `text-size` — Text size. - Animatable - Type: `number` - Default: `20` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` Only with x plane: - `x` — X coordinate. On the ordinary plane and (x alone) the number line. - Implicit, Animatable - Type: `parametised-number` - Shorthand: `point:value` Only with xy plane: - `x` — X coordinate. On the ordinary plane and (x alone) the number line. - Implicit, Animatable - Type: `parametised-number` - Shorthand: `point:value` - `y` — Y coordinate. On the ordinary plane and (x alone) the number line. - Animatable - Type: `parametised-number` Only with argand plane: - `z` — On the argand plane, the position as one complex number — a complex expression: `3 + 2i`, `(3 + 2i)^2`, `sqrt(13) * e^(i * a)`, with `i` the unit and parameters read as complex. Instead of `re`/`im`. - Animatable - Type: `complex-expression` - `re` — On the argand plane, the real part of the position, a number; with `im`. `z:` gives both at once. - Animatable - Type: `parametised-number` - `im` — On the argand plane, the imaginary part of the position, a number; with `re`. `z:` gives both at once. - Animatable - Type: `parametised-number` Only with polar plane: - `r` — On the polar plane, the distance from the origin; with `theta`. - Animatable - Type: `parametised-number` - `theta` — On the polar plane, the angle anticlockwise from the right, in radians — `60deg` writes it in degrees; with `r`. On a categorical theta (`theta format:`), a name. - Animatable - Type: `parametised-number` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk point(x: 1; y: 2) ``` ```chalk point(x: -3; y: 1; size: 8; colour: orange) ``` --- # polygon A closed shape whose outline is its `x:` and `y:` vertex series, paired by position. The named shapes take a position and a size; a polygon takes its outline directly. - Kind: Node - Section: Graph environment › Marks - Only with xy, argand or polar plane ## Attributes On any plane: - `period` — Long-format data: one row per x per period, this naming the period column, sliced at the environment's parameter over that column. - Type: `reference` - Also written: `at` - `fill` — Fill polygon. - Animatable - Type: `boolean` - Also written: `filled` - Default: `true` - `colour` — Colour. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `teal` - `opacity` — Opacity. - Animatable - Type: `number` - Default: `1` - `dashed` — Draws the outline dashed. What it is for is the shape that is not there: where a figure was before it moved, a region a constraint rules out, a construction that explains the drawing without being part of it. - Animatable - Type: `boolean` - Default: `false` - `interpolate` — How the outline gets from one vertex to the next: `linear` for straight sides, `bezier` for a closed round outline, on the same midpoint handles as the line’s. No `step` — a staircase is a reading against x, and a closed outline is not one. - Animatable - Options: `linear`, `bezier` - Also written: `interpolation` - Default: `linear` - `label` — Text drawn beside the element — rich text, so `$maths$` and inline formatting render. Fades and draws with the element. - Type: `rich-text` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and unlike a `cue`, a hidden element still frames the plot, so the axes do not jump when it appears. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. Nothing at all on a graph that is not editable. - Type: `boolean` - Also written: `locked` - Default: `false` - `text-size` — Text size. - Animatable - Type: `number` - Default: `20` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` Only with xy plane: - `x` — The vertices' x coordinates, e.g. `0, 2, 1`. Numbers or a dataset column; entries may be expressions of parameters. - Implicit, Animatable - Type: `series` - Shorthand: `polygon:value` - `y` — The vertices' y coordinates, paired with `x` by position. - Animatable - Type: `series` Only with argand plane: - `z` — On the argand plane, the readings as complex numbers: written out (`1 + i, 2 - i, -3i`, a bare number a real), or a column of complex literals bound whole (`#roots.z`). Instead of `re`/`im`. - Animatable - Type: `complex-series` - `re` — On the argand plane, the readings' real parts, one per reading — numbers written out or a column — paired with `im` by position. - Animatable - Type: `series` - `im` — On the argand plane, the readings' imaginary parts, paired with `re` by position. - Animatable - Type: `series` Only with polar plane: - `r` — On the polar plane, the readings' distances from the origin, paired with `theta` by position. - Animatable - Type: `series` - `theta` — On the polar plane, the readings' angles in radians (`60deg` for degrees), paired with `r` by position; names on a categorical theta. - Animatable - Type: `series` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk polygon(x: 0, 2, 1; y: 0, 0, 2) ``` ```chalk polygon(x: -1, 1, 0; y: -1, -1, 1; fill: false; colour: teal) ``` --- # rectangle A rectangle centred at (x, y), scaled by `size`. One of seven shape marks that differ only in the outline drawn. - Kind: Node - Section: Graph environment › Marks - Family: Shapes - Only with xy, argand or polar plane ## Attributes On any plane: - `size` — Half the width. The height is fixed at 0.6 of the width, so the rectangle is 2×size wide and 1.2×size tall. - Animatable - Type: `parametised-number` - Default: `1` - `colour` — Colour. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `purple` - `fill` — Fill shape. - Animatable - Type: `boolean` - Default: `true` - `dashed` — Draws the outline dashed — the shape that is not there: a construction, a ghost of a previous position, a region ruled out. As `polygon`'s and `segment`'s. - Animatable - Type: `boolean` - Default: `false` - `opacity` — Opacity. - Animatable - Type: `number` - Default: `1` - `label` — Text drawn beside the element — rich text, so `$maths$` and inline formatting render. Fades and draws with the element. - Type: `rich-text` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and unlike a `cue`, a hidden element still frames the plot, so the axes do not jump when it appears. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. Nothing at all on a graph that is not editable. - Type: `boolean` - Also written: `locked` - Default: `false` - `text-size` — Text size. - Animatable - Type: `number` - Default: `20` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` Only with xy plane: - `x` — X coordinate. - Implicit, Animatable - Type: `parametised-number` - Shorthand: `rectangle:value` - `y` — Y coordinate. - Animatable - Type: `parametised-number` Only with argand plane: - `z` — On the argand plane, the position as one complex number — a complex expression: `3 + 2i`, `(3 + 2i)^2`, `sqrt(13) * e^(i * a)`, with `i` the unit and parameters read as complex. Instead of `re`/`im`. - Animatable - Type: `complex-expression` - `re` — On the argand plane, the real part of the position, a number; with `im`. `z:` gives both at once. - Animatable - Type: `parametised-number` - `im` — On the argand plane, the imaginary part of the position, a number; with `re`. `z:` gives both at once. - Animatable - Type: `parametised-number` Only with polar plane: - `r` — On the polar plane, the distance from the origin; with `theta`. - Animatable - Type: `parametised-number` - `theta` — On the polar plane, the angle anticlockwise from the right, in radians — `60deg` writes it in degrees; with `r`. On a categorical theta (`theta format:`), a name. - Animatable - Type: `parametised-number` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk rectangle(x: 0; y: 0; size: 2) ``` ```chalk rectangle(x: 2; y: 2; size: 1.4; fill: false) ``` --- # residuals Dashed drops from each of a fit's own observations to the fitted curve. `of:` names the fit; omitted, it takes the graph's only fit, and a graph with more than one has to be told which. - Kind: Node - Section: Graph environment › Marks - Only with xy plane Residuals draw the vertical differences from a `fit` to the points it was found from: `residuals(of: #trend; colour: orange)`, or just `residuals` where the graph holds a single fit. ## Attributes - `of` — The fit to measure from, by its id — `residuals(of: #trend)`. The points drawn are that fit's own. Omitted, it takes the graph's only fit. - Implicit - Type: `reference` - Shorthand: `residuals:value` - `colour` — Colour. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `orange` - `width` — Stroke width. - Animatable - Type: `number` - Default: `1.5` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and unlike a `cue`, a hidden element still frames the plot, so the axes do not jump when it appears. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. Nothing at all on a graph that is not editable. - Type: `boolean` - Also written: `locked` - Default: `false` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk cue{ residuals(of: #trend; colour: orange) }( in: 1 ) ``` --- # scatter A set of points, one axis per attribute: `scatter(x: 0, 1, 2; y: 0, 2, 1)` — or dataset columns, `scatter(x: #islands.area; y: #islands.species)`. One shape, size and colour apply to all of them. A fit in the same environment fits this data. A scatter STANDS when it has a line to lay its dots out about, as a `boxplot` does: in a lane above the number line, where it is a dot plot; at its `on:` on a plane, as a strip of one sample; or, written with both coordinates and one axis ruled in names, at every name at once, each reading at the name in its row. `layout:` lays the dots out about that line — stacked, swarmed or jittered — and a scatter and a box of the same readings at the same place share one slot, so the box lands on the dots it summarises. `split:` colours the readings on either side of a value apart, the diverging picture of paired comparisons. - Kind: Node - Section: Graph environment › Marks ## Readings A scatter takes its readings one axis per attribute, paired by position — `scatter(x: 1, 2, 3; y: 2.0, 2.4, 3.1)` — and either attribute binds a dataset column directly: `scatter(x: #islands.area; y: #islands.species)`. On the number line the same readings are a distribution: `scatter(x: 2, 3, 3, 4)` stacks equal values into a dot plot. ## Laying out the dots Where a scatter stands — a lane on the number line, or a place given by `on:` — `layout` says how its dots are laid out: `stack`, the number line's default; `swarm`, which puts each dot at the nearest place across the line where it touches no other, so readings that are merely close spread as well as ones that are equal and the outline of the swarm is the shape of the distribution; `jitter`, a settled offset for each dot, which is what a sample of thousands has to be drawn as; or `none`. A scatter standing at a place on a plane is swarmed unless `layout` says otherwise. `width` is the share of the lane a swarm or a jitter may spread across. A swarm too crowded for its `width` — a column of tied readings, say — draws its dots smaller until it fits, down to half the size asked for, before it lets any overlap. A scatter over two numeric axes stands nowhere, so it takes no `stack` or `swarm`; `layout: jitter` there nudges both coordinates, by at most half the smallest gap between the values on each axis, so tied readings come apart without any dot crossing the place a different reading would have been. The offsets come from the graph's `seed`. ## Colouring by a threshold Given a `split`, a scatter colours the readings at or above it in `split colour` and leaves the rest in `colour` — the diverging picture of paired comparisons, every one on the side of no difference it fell, read from the reading itself so a swarmed or jittered dot keeps the colour of its value. The `statistics` pack's `effect-strip` draws that whole chart, the baseline and both named sides included. ```chalk canvas.graph:xy{ axis.label(x: 0; label: no change; colour: grey; line) boxplot(x: #studies.change; y: #studies.impact; colour: grey) scatter(x: #studies.change; y: #studies.impact; colour: purple; split: 0; split colour: pink) }( x axis label: change per kilogram of food (%) ) ``` ## Error bars A scatter takes `error` and `error-x`, one half-width per reading — a column of uncertainties bound whole draws a capped bar through every dot, and the frame grows to hold the caps. ## Attributes On any plane: - `period` — Long-format data: one row per x per period, this naming the period column. The points then show the slice at whatever the environment's parameter over that column holds, gliding between the two periods that bracket it. - Type: `reference` - Also written: `at` - `error` — The uncertainty on each reading up the y axis, drawn as a bar through its dot with a cap at each end. HALF-widths: an error of 0.3 on a reading of 4.2 spans 3.9 to 4.5. One entry per reading, paired by position as the coordinates are — or a column bound whole, which is the usual case, since a measurement's uncertainty arrives in the file beside it. Nothing is drawn where an entry is missing. Refused on the number line, which has no y for a reading to be uncertain in, and on the polar plane, where a bar across a radius or an angle is not the interval it stands for. - Animatable - Type: `series` - Also written: `error-y` - `error-x` — The same, along the x axis — the uncertainty a reading on a number line can have, and the horizontal half of an error cross on a plane. Both may be given at once. - Animatable - Type: `series` - `shape` — Point shape. - Options: `circle`, `square`, `rectangle`, `triangle`, `star`, `diamond`, `hexagon`, `cross` - Default: `circle` - `size` — Point size. - Animatable - Type: `parametised-number` - Default: `6` - `colour` — Colour. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `red` - `opacity` — Opacity. - Animatable - Type: `number` - Default: `1` - `label` — Text drawn beside the element — rich text, so `$maths$` and inline formatting render. Fades and draws with the element. - Type: `rich-text` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and unlike a `cue`, a hidden element still frames the plot, so the axes do not jump when it appears. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. Nothing at all on a graph that is not editable. - Type: `boolean` - Also written: `locked` - Default: `false` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` Only with x plane: - `x` — The observations' x values: numbers, or a dataset column (`#islands.area`). A column of names reads against a categorical axis (`x format:`, or the axis’s `auto` rule); an ISO date column reads as fractional years (`2024-03-15` → ≈2024.2), and `auto` labels them in years. Paired with `y` by position; a pair either axis cannot read as a number is dropped whole. - Implicit, Animatable - Type: `series` - Shorthand: `scatter:value` - `layout` — How the dots of a standing scatter are laid out across the line they stand on. `stack` stacks equal readings, so the height of a column is the count: the dot plot. `swarm` puts each dot at the nearest place across the line where it touches no other, so readings that are merely close spread as well as equal ones and the outline is the shape of the distribution. `jitter` gives each dot a settled offset within `width:`; on a scatter that does not stand it nudges both coordinates instead, by at most half the smallest gap between the values on each axis. `none` puts every dot on the line. `auto`, the default, is a `stack` on the number line, a `swarm` beside a box of the same readings or at a name or an `on:`, and `none` on an ordinary cloud. `stack` and `swarm` are refused on a scatter that stands nowhere, and everything but `auto` and `none` on the polar plane. Animatable: a `!cue.to` from one layout to another glides each dot from where the old layout put it to where the new one does. - Animatable - Options: `auto`, `none`, `stack`, `swarm`, `jitter` - Default: `auto` - `width` — The share of its room a swarm or a jittered strip may spread across — the same room a `boxplot`'s `width:` is a share of, so a box and its dots are measured against one thing. A swarm that needs more draws its dots smaller until it fits, one size for the whole mark and never below half the size asked for, and only then overlaps at its edge rather than spilling into the next name. On an ordinary cloud, the share of the half-gap `jitter` may nudge a dot by. Nothing to `stack` or `none`. - Animatable - Type: `number` - Default: `0.8` - `split` — A value that divides the readings in two, each side in its own colour: readings below it take `colour:`, readings at or above it take `split-colour:`. `split: 0` beside a purple and a pink is the diverging picture, every comparison on the side of no difference it fell. Measured on the axis readings are measured along: the line a standing scatter is laid out about, x on the number line, y on an ordinary cloud. Read from the reading itself, so a jittered dot stays the colour of its value, and each error bar takes its reading's colour. Refused without `split-colour:`, and on the polar plane. - Animatable - Type: `parametised-number` - `split-colour` — The colour of the readings at or above `split:`. Refused without a `split:`. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `split-color` Only with xy plane: - `x` — The observations' x values: numbers, or a dataset column (`#islands.area`). A column of names reads against a categorical axis (`x format:`, or the axis’s `auto` rule); an ISO date column reads as fractional years (`2024-03-15` → ≈2024.2), and `auto` labels them in years. Paired with `y` by position; a pair either axis cannot read as a number is dropped whole. - Implicit, Animatable - Type: `series` - Shorthand: `scatter:value` - `y` — The observations' y values, paired with `x` by position. - Animatable - Type: `series` - `on` — Where a strip of ONE sample stands on the other axis — a number, a name, or a date, as a `boxplot`'s `on:` takes. `scatter(y: #trial.control; on: control)` is the readings up the y axis, all standing at the name `control` on x, and a name there joins the axis's names. A `boxplot` with the same readings and the same `on:` shares the strip's slot. Refused on the number line, which has no second axis, and on a scatter that writes both coordinates, whose dots stand where their readings put them. - Animatable - Type: `axis-value` - `layout` — How the dots of a standing scatter are laid out across the line they stand on. `stack` stacks equal readings, so the height of a column is the count: the dot plot. `swarm` puts each dot at the nearest place across the line where it touches no other, so readings that are merely close spread as well as equal ones and the outline is the shape of the distribution. `jitter` gives each dot a settled offset within `width:`; on a scatter that does not stand it nudges both coordinates instead, by at most half the smallest gap between the values on each axis. `none` puts every dot on the line. `auto`, the default, is a `stack` on the number line, a `swarm` beside a box of the same readings or at a name or an `on:`, and `none` on an ordinary cloud. `stack` and `swarm` are refused on a scatter that stands nowhere, and everything but `auto` and `none` on the polar plane. Animatable: a `!cue.to` from one layout to another glides each dot from where the old layout put it to where the new one does. - Animatable - Options: `auto`, `none`, `stack`, `swarm`, `jitter` - Default: `auto` - `width` — The share of its room a swarm or a jittered strip may spread across — the same room a `boxplot`'s `width:` is a share of, so a box and its dots are measured against one thing. A swarm that needs more draws its dots smaller until it fits, one size for the whole mark and never below half the size asked for, and only then overlaps at its edge rather than spilling into the next name. On an ordinary cloud, the share of the half-gap `jitter` may nudge a dot by. Nothing to `stack` or `none`. - Animatable - Type: `number` - Default: `0.8` - `split` — A value that divides the readings in two, each side in its own colour: readings below it take `colour:`, readings at or above it take `split-colour:`. `split: 0` beside a purple and a pink is the diverging picture, every comparison on the side of no difference it fell. Measured on the axis readings are measured along: the line a standing scatter is laid out about, x on the number line, y on an ordinary cloud. Read from the reading itself, so a jittered dot stays the colour of its value, and each error bar takes its reading's colour. Refused without `split-colour:`, and on the polar plane. - Animatable - Type: `parametised-number` - `split-colour` — The colour of the readings at or above `split:`. Refused without a `split:`. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `split-color` Only with argand plane: - `z` — On the argand plane, the readings as complex numbers: written out (`1 + i, 2 - i, -3i`, a bare number a real), or a column of complex literals bound whole (`#roots.z`). Instead of `re`/`im`. - Animatable - Type: `complex-series` - `re` — On the argand plane, the readings' real parts, one per reading — numbers written out or a column — paired with `im` by position. - Animatable - Type: `series` - `im` — On the argand plane, the readings' imaginary parts, paired with `re` by position. - Animatable - Type: `series` - `on` — Where a strip of ONE sample stands on the other axis — a number, a name, or a date, as a `boxplot`'s `on:` takes. `scatter(y: #trial.control; on: control)` is the readings up the y axis, all standing at the name `control` on x, and a name there joins the axis's names. A `boxplot` with the same readings and the same `on:` shares the strip's slot. Refused on the number line, which has no second axis, and on a scatter that writes both coordinates, whose dots stand where their readings put them. - Animatable - Type: `axis-value` - `layout` — How the dots of a standing scatter are laid out across the line they stand on. `stack` stacks equal readings, so the height of a column is the count: the dot plot. `swarm` puts each dot at the nearest place across the line where it touches no other, so readings that are merely close spread as well as equal ones and the outline is the shape of the distribution. `jitter` gives each dot a settled offset within `width:`; on a scatter that does not stand it nudges both coordinates instead, by at most half the smallest gap between the values on each axis. `none` puts every dot on the line. `auto`, the default, is a `stack` on the number line, a `swarm` beside a box of the same readings or at a name or an `on:`, and `none` on an ordinary cloud. `stack` and `swarm` are refused on a scatter that stands nowhere, and everything but `auto` and `none` on the polar plane. Animatable: a `!cue.to` from one layout to another glides each dot from where the old layout put it to where the new one does. - Animatable - Options: `auto`, `none`, `stack`, `swarm`, `jitter` - Default: `auto` - `width` — The share of its room a swarm or a jittered strip may spread across — the same room a `boxplot`'s `width:` is a share of, so a box and its dots are measured against one thing. A swarm that needs more draws its dots smaller until it fits, one size for the whole mark and never below half the size asked for, and only then overlaps at its edge rather than spilling into the next name. On an ordinary cloud, the share of the half-gap `jitter` may nudge a dot by. Nothing to `stack` or `none`. - Animatable - Type: `number` - Default: `0.8` - `split` — A value that divides the readings in two, each side in its own colour: readings below it take `colour:`, readings at or above it take `split-colour:`. `split: 0` beside a purple and a pink is the diverging picture, every comparison on the side of no difference it fell. Measured on the axis readings are measured along: the line a standing scatter is laid out about, x on the number line, y on an ordinary cloud. Read from the reading itself, so a jittered dot stays the colour of its value, and each error bar takes its reading's colour. Refused without `split-colour:`, and on the polar plane. - Animatable - Type: `parametised-number` - `split-colour` — The colour of the readings at or above `split:`. Refused without a `split:`. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `split-color` Only with polar plane: - `r` — On the polar plane, the readings' distances from the origin, paired with `theta` by position. - Animatable - Type: `series` - `theta` — On the polar plane, the readings' angles in radians (`60deg` for degrees), paired with `r` by position; names on a categorical theta. - Animatable - Type: `series` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk scatter(x: 0, 1, 2; y: 0, 2, 1) ``` ```chalk scatter(x: -1, 0, 1; y: 1, 0, -1; shape: square; colour: red) ``` ```chalk scatter(x: #islands.area; y: #islands.species) ``` ```chalk scatter(x: #studies.change; y: #studies.impact; layout: swarm) ``` ```chalk scatter(y: #trial.control; on: control) ``` ```chalk scatter(x: #studies.change; y: #studies.impact; colour: purple; split: 0; split-colour: pink) ``` --- # segment A straight line between two points: `segment(start: (0, 0); end: (2, 1))`. `start` is the implicit attribute. Unlike a line, it stops at its endpoints rather than running on to the frame. - Kind: Node - Section: Graph environment › Marks A segment takes `ticks`, the congruence hatches across its middle: two sides marked `ticks: 1` are the same length, which is how a construction says so — the notation an `angle` already has in `arcs`. Like every outline it takes `dashed`, for the line that is *not* there: where a side was before it moved, or a construction that explains the drawing without being part of it. ## Attributes - `start` — One endpoint, as a coordinate: `start: (2, 1)`. Animatable as a whole, so `!cue.to{(4, 3)}` moves the end in a straight line. - Required, Implicit, Animatable - Type: `parametised-coordinate-list` - Also written: `from` - Shorthand: `segment:value` - `end` — The other endpoint, e.g. `end: (6, 9)`. - Required, Animatable - Type: `parametised-coordinate-list` - Also written: `to` - `colour` — Colour. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `grey` - `width` — Stroke width. - Animatable - Type: `number` - Default: `2` - `opacity` — Opacity. - Animatable - Type: `number` - Default: `1` - `dashed` — Dashed stroke. - Animatable - Type: `boolean` - Default: `false` - `ticks` — Congruence hatches across the middle of the segment: one tick, two, three. Two sides carrying the same number of ticks are the same length, which is how a construction says so — the notation `angle`'s `arcs` already has for angles. A count rather than a flag, because the second pair in a figure needs a second mark. - Animatable - Type: `number` - Default: `0` - `label` — Text drawn beside the element — rich text, so `$maths$` and inline formatting render. Fades and draws with the element. - Type: `rich-text` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and unlike a `cue`, a hidden element still frames the plot, so the axes do not jump when it appears. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. Nothing at all on a graph that is not editable. - Type: `boolean` - Also written: `locked` - Default: `false` - `text-size` — Text size. - Animatable - Type: `number` - Default: `20` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk segment(start: (0, 0); end: (2, 1)) ``` ```chalk segment(start: (-2, 1); end: (1, -1); colour: grey) ``` --- # square A square centred at (x, y), scaled by `size`. One of seven shape marks that differ only in the outline drawn. - Kind: Node - Section: Graph environment › Marks - Family: Shapes - Only with xy, argand or polar plane ## Attributes On any plane: - `size` — Half the side length: corners sit at (x ± size, y ± size), so the square is 2×size across. - Animatable - Type: `parametised-number` - Default: `1` - `colour` — Colour. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `purple` - `fill` — Fill shape. - Animatable - Type: `boolean` - Default: `true` - `dashed` — Draws the outline dashed — the shape that is not there: a construction, a ghost of a previous position, a region ruled out. As `polygon`'s and `segment`'s. - Animatable - Type: `boolean` - Default: `false` - `opacity` — Opacity. - Animatable - Type: `number` - Default: `1` - `label` — Text drawn beside the element — rich text, so `$maths$` and inline formatting render. Fades and draws with the element. - Type: `rich-text` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and unlike a `cue`, a hidden element still frames the plot, so the axes do not jump when it appears. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. Nothing at all on a graph that is not editable. - Type: `boolean` - Also written: `locked` - Default: `false` - `text-size` — Text size. - Animatable - Type: `number` - Default: `20` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` Only with xy plane: - `x` — X coordinate. - Implicit, Animatable - Type: `parametised-number` - Shorthand: `square:value` - `y` — Y coordinate. - Animatable - Type: `parametised-number` Only with argand plane: - `z` — On the argand plane, the position as one complex number — a complex expression: `3 + 2i`, `(3 + 2i)^2`, `sqrt(13) * e^(i * a)`, with `i` the unit and parameters read as complex. Instead of `re`/`im`. - Animatable - Type: `complex-expression` - `re` — On the argand plane, the real part of the position, a number; with `im`. `z:` gives both at once. - Animatable - Type: `parametised-number` - `im` — On the argand plane, the imaginary part of the position, a number; with `re`. `z:` gives both at once. - Animatable - Type: `parametised-number` Only with polar plane: - `r` — On the polar plane, the distance from the origin; with `theta`. - Animatable - Type: `parametised-number` - `theta` — On the polar plane, the angle anticlockwise from the right, in radians — `60deg` writes it in degrees; with `r`. On a categorical theta (`theta format:`), a name. - Animatable - Type: `parametised-number` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk square(x: 0; y: 0; size: 2) ``` ```chalk square(x: 1; y: -1; size: 1.2; opacity: 0.6) ``` --- # star A five-pointed star centred at (x, y), scaled by `size`. One of seven shape marks that differ only in the outline drawn. - Kind: Node - Section: Graph environment › Marks - Family: Shapes - Only with xy, argand or polar plane ## Attributes On any plane: - `size` — Radius of the five outer points. The inner points sit at 0.4 of it. - Animatable - Type: `parametised-number` - Default: `1` - `colour` — Colour. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `purple` - `fill` — Fill shape. - Animatable - Type: `boolean` - Default: `true` - `dashed` — Draws the outline dashed — the shape that is not there: a construction, a ghost of a previous position, a region ruled out. As `polygon`'s and `segment`'s. - Animatable - Type: `boolean` - Default: `false` - `opacity` — Opacity. - Animatable - Type: `number` - Default: `1` - `label` — Text drawn beside the element — rich text, so `$maths$` and inline formatting render. Fades and draws with the element. - Type: `rich-text` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and unlike a `cue`, a hidden element still frames the plot, so the axes do not jump when it appears. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. Nothing at all on a graph that is not editable. - Type: `boolean` - Also written: `locked` - Default: `false` - `text-size` — Text size. - Animatable - Type: `number` - Default: `20` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` Only with xy plane: - `x` — X coordinate. - Implicit, Animatable - Type: `parametised-number` - Shorthand: `star:value` - `y` — Y coordinate. - Animatable - Type: `parametised-number` Only with argand plane: - `z` — On the argand plane, the position as one complex number — a complex expression: `3 + 2i`, `(3 + 2i)^2`, `sqrt(13) * e^(i * a)`, with `i` the unit and parameters read as complex. Instead of `re`/`im`. - Animatable - Type: `complex-expression` - `re` — On the argand plane, the real part of the position, a number; with `im`. `z:` gives both at once. - Animatable - Type: `parametised-number` - `im` — On the argand plane, the imaginary part of the position, a number; with `re`. `z:` gives both at once. - Animatable - Type: `parametised-number` Only with polar plane: - `r` — On the polar plane, the distance from the origin; with `theta`. - Animatable - Type: `parametised-number` - `theta` — On the polar plane, the angle anticlockwise from the right, in radians — `60deg` writes it in degrees; with `r`. On a categorical theta (`theta format:`), a name. - Animatable - Type: `parametised-number` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk star(x: 0; y: 0; size: 2) ``` ```chalk star(x: 1; y: 1; size: 1; fill: false) ``` --- # text Freestanding text at (x, y) — a mark that is nothing but its label. It carries annotations belonging to the plot rather than to any one mark: a region's name, a caption inside the frame, a worked value. - Kind: Node - Section: Graph environment › Marks - Writing with it: Framing a readable graph ## Attributes On any plane: - `label` — The text itself — rich, so `$maths$` and inline formatting render. - Required, Implicit - Type: `rich-text` - Shorthand: `text:value` - `size` — Text size. - Animatable - Type: `number` - Default: `20` - `colour` — Colour. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `neutral` - `anchor` — How the text sits on its point: `middle` centres it, `start` and `end` put its left or right edge there. - Type: `string` - Default: `middle` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and a hidden element still frames the plot, so the axes do not jump when it appears. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. - Type: `boolean` - Also written: `locked` - Default: `false` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` Only with x plane: - `x` — X coordinate of the anchor. - Animatable - Type: `parametised-number` Only with xy plane: - `x` — X coordinate of the anchor. - Animatable - Type: `parametised-number` - `y` — Y coordinate of the anchor. - Animatable - Type: `parametised-number` Only with argand plane: - `z` — On the argand plane, the position as one complex number — a complex expression: `3 + 2i`, `(3 + 2i)^2`, `sqrt(13) * e^(i * a)`, with `i` the unit and parameters read as complex. Instead of `re`/`im`. - Animatable - Type: `complex-expression` - `re` — On the argand plane, the real part of the position, a number; with `im`. `z:` gives both at once. - Animatable - Type: `parametised-number` - `im` — On the argand plane, the imaginary part of the position, a number; with `re`. `z:` gives both at once. - Animatable - Type: `parametised-number` Only with polar plane: - `r` — On the polar plane, the distance from the origin; with `theta`. - Animatable - Type: `parametised-number` - `theta` — On the polar plane, the angle anticlockwise from the right, in radians — `60deg` writes it in degrees; with `r`. On a categorical theta (`theta format:`), a name. - Animatable - Type: `parametised-number` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk text(label: local maximum; x: 2; y: 4.5) ``` ```chalk text(label: $R^2 = 0.97$; x: 8; y: 1; anchor: start) ``` --- # triangle A triangle centred at (x, y), scaled by `size`. One of seven shape marks that differ only in the outline drawn. - Kind: Node - Section: Graph environment › Marks - Family: Shapes - Only with xy, argand or polar plane ## Attributes On any plane: - `size` — Distance from the centre to each vertex. The centre sits a quarter of that below the midpoint, so the triangle reads as balanced. - Animatable - Type: `parametised-number` - Default: `1` - `colour` — Colour. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `purple` - `fill` — Fill shape. - Animatable - Type: `boolean` - Default: `true` - `dashed` — Draws the outline dashed — the shape that is not there: a construction, a ghost of a previous position, a region ruled out. As `polygon`'s and `segment`'s. - Animatable - Type: `boolean` - Default: `false` - `opacity` — Opacity. - Animatable - Type: `number` - Default: `1` - `label` — Text drawn beside the element — rich text, so `$maths$` and inline formatting render. Fades and draws with the element. - Type: `rich-text` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and unlike a `cue`, a hidden element still frames the plot, so the axes do not jump when it appears. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. Nothing at all on a graph that is not editable. - Type: `boolean` - Also written: `locked` - Default: `false` - `text-size` — Text size. - Animatable - Type: `number` - Default: `20` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` Only with xy plane: - `x` — X coordinate. - Implicit, Animatable - Type: `parametised-number` - Shorthand: `triangle:value` - `y` — Y coordinate. - Animatable - Type: `parametised-number` Only with argand plane: - `z` — On the argand plane, the position as one complex number — a complex expression: `3 + 2i`, `(3 + 2i)^2`, `sqrt(13) * e^(i * a)`, with `i` the unit and parameters read as complex. Instead of `re`/`im`. - Animatable - Type: `complex-expression` - `re` — On the argand plane, the real part of the position, a number; with `im`. `z:` gives both at once. - Animatable - Type: `parametised-number` - `im` — On the argand plane, the imaginary part of the position, a number; with `re`. `z:` gives both at once. - Animatable - Type: `parametised-number` Only with polar plane: - `r` — On the polar plane, the distance from the origin; with `theta`. - Animatable - Type: `parametised-number` - `theta` — On the polar plane, the angle anticlockwise from the right, in radians — `60deg` writes it in degrees; with `r`. On a categorical theta (`theta format:`), a name. - Animatable - Type: `parametised-number` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk triangle(x: 0; y: 0; size: 2) ``` ```chalk triangle(x: -2; y: 1; size: 1; colour: orange) ``` --- # vector An arrow from (x, y) along (dx, dy). `dx` is the implicit attribute, and the tail sits at the origin unless x and y move it. - Kind: Node - Section: Graph environment › Marks ## Attributes On any plane: - `colour` — Colour. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `green` - `width` — Stroke width. - Animatable - Type: `number` - Default: `2` - `opacity` — Opacity. - Animatable - Type: `number` - Default: `1` - `dashed` — Dashed stroke. - Animatable - Type: `boolean` - Default: `false` - `label` — Text drawn beside the element — rich text, so `$maths$` and inline formatting render. Fades and draws with the element. - Type: `rich-text` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and unlike a `cue`, a hidden element still frames the plot, so the axes do not jump when it appears. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. Nothing at all on a graph that is not editable. - Type: `boolean` - Also written: `locked` - Default: `false` - `text-size` — Text size. - Animatable - Type: `number` - Default: `20` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). Written `z:` before the argand plane took that letter for its coordinate. - Animatable - Type: `parametised-number` Only with x plane: - `dx` — X component. Cartesian on the ordinary and polar planes alike; on the argand plane the displacement is `z:`. - Implicit, Animatable - Type: `parametised-number` - Shorthand: `vector:value` - `dy` — Y component. Cartesian on the ordinary and polar planes alike; on the argand plane the displacement is `z:`. - Animatable - Type: `parametised-number` - `x` — X coordinate of the tail. Defaults to 0, so a vector with only dx and dy starts at the origin. - Animatable - Type: `parametised-number` Only with xy plane: - `dx` — X component. Cartesian on the ordinary and polar planes alike; on the argand plane the displacement is `z:`. - Implicit, Animatable - Type: `parametised-number` - Shorthand: `vector:value` - `dy` — Y component. Cartesian on the ordinary and polar planes alike; on the argand plane the displacement is `z:`. - Animatable - Type: `parametised-number` - `x` — X coordinate of the tail. Defaults to 0, so a vector with only dx and dy starts at the origin. - Animatable - Type: `parametised-number` - `y` — Y coordinate of the tail. - Animatable - Type: `parametised-number` Only with argand plane: - `z` — On the argand plane, the number the arrow IS — a complex expression (`3 + 2i`, `(1 + i)^2`, `e^(i * a)`, `i` the unit) drawn from the tail out by this displacement. `dx`/`dy` are not read there. - Animatable - Type: `complex-expression` - `re` — On the argand plane, the real part of the position, a number; with `im`. `z:` gives both at once. - Animatable - Type: `parametised-number` - `im` — On the argand plane, the imaginary part of the position, a number; with `re`. `z:` gives both at once. - Animatable - Type: `parametised-number` Only with polar plane: - `dx` — X component. Cartesian on the ordinary and polar planes alike; on the argand plane the displacement is `z:`. - Implicit, Animatable - Type: `parametised-number` - Shorthand: `vector:value` - `dy` — Y component. Cartesian on the ordinary and polar planes alike; on the argand plane the displacement is `z:`. - Animatable - Type: `parametised-number` - `r` — On the polar plane, the distance from the origin; with `theta`. - Animatable - Type: `parametised-number` - `theta` — On the polar plane, the angle anticlockwise from the right, in radians — `60deg` writes it in degrees; with `r`. On a categorical theta (`theta format:`), a name. - Animatable - Type: `parametised-number` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk vector(x: 0; y: 0; dx: 3; dy: 2) ``` ```chalk vector(x: -1; y: -1; dx: 2; dy: 1; dashed: true) ``` --- # violin A sample drawn as the shape of its density: a kernel density estimate of the readings, mirrored about the line the violin stands on, wide where readings crowd and narrow where they thin, and cut off square at the smallest and largest reading rather than drawn past them. Where a box keeps five numbers and throws the shape away, a violin keeps the shape, so two humps are two humps. It stands exactly as a `boxplot` does — a lane above the number line, one sample at an `on:`, or a violin at every name where its other coordinate is a column of names — and a violin, a box and a scatter of the same readings at one place share one slot, so a narrow box inside its violin is two lines. Each violin is as wide as `width:` of its slot at its own peak, so the shapes compare and the counts do not. Refused with both coordinates numeric, which leaves no line to stand on, and on the polar plane. - Kind: Node - Section: Graph environment › Marks - Only with x, xy or argand plane A violin draws readings as the shape of their density: a kernel density estimate, mirrored about the line the violin stands on, wide where readings crowd and narrow where they thin, and cut off at the smallest and largest reading rather than drawn past them. Where a `boxplot` keeps five numbers and throws the shape away, a violin keeps the shape, so two humps are two humps. It stands exactly as a box does, and a violin, a box and a scatter of the same readings at one place share one slot, so a narrow box inside its violin is two lines. `bandwidth` is how far each reading is smoothed. Left out, each sample takes Silverman's rule of thumb; given a parameter, a reader can drag it and watch bumps appear and dissolve, which is most of what a violin has to teach. Each violin is as wide as `width` of its slot at its own peak, so the shapes compare and the counts do not — the count behind a violin is what a swarm over it shows. ```chalk canvas.graph:xy{ violin(x: #penguins.flipper; y: #penguins.species; colour: purple) boxplot(x: #penguins.flipper; y: #penguins.species; width: 0.15; colour: teal) }( x axis label: flipper length (mm) ) ``` ## Attributes On any plane: - `bandwidth` — The width of the kernel each reading is smoothed over, in the readings' own units. Left out, each sample takes Silverman's rule of thumb, 0.9 × min(σ, IQR ÷ 1.34) × n^(−1/5). Smaller shows every bump in the sample and larger smooths them away; parametised, so a reader dragging a slider watches bumps appear and dissolve. A written number must be positive. - Animatable - Type: `parametised-number` - `colour` — Colour. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `purple` - `width` — The share of its slot a violin is wide at its peak — the same room a `boxplot`'s and a `scatter`'s `width:` are shares of, so a narrow box sits inside a wide violin by giving each its own share. - Animatable - Type: `number` - Default: `0.8` - `opacity` — Opacity. - Animatable - Type: `number` - Default: `1` - `label` — Text drawn beside the element — rich text, so `$maths$` and inline formatting render. Fades and draws with the element. - Type: `rich-text` - `hidden` — Declared but not drawn. Animatable — `hidden: true !cue.to{false}(at: 2)` reveals it — and unlike a `cue`, a hidden element still frames the plot and holds its slot, so nothing moves when it appears. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing this element on an `editable:` graph. Nothing at all on a graph that is not editable. - Type: `boolean` - Also written: `locked` - Default: `false` - `text-size` — Text size. - Animatable - Type: `number` - Default: `20` - `layer` — Paint order among the marks: a higher layer is drawn later, on top; equal or omitted, the marks stack in document order (later on top). - Animatable - Type: `parametised-number` Only with x plane: - `x` — The readings — numbers written out, or a dataset column bound whole. The sample itself, not a density worked out by hand. One coordinate is one sample; `x:` and `y:` together, with one axis ruled in names, are a sample grouped by those names, a violin at each. - Implicit, Animatable - Type: `series` - Shorthand: `violin:value` Only with xy plane: - `x` — The readings — numbers written out, or a dataset column bound whole. The sample itself, not a density worked out by hand. One coordinate is one sample; `x:` and `y:` together, with one axis ruled in names, are a sample grouped by those names, a violin at each. - Implicit, Animatable - Type: `series` - Shorthand: `violin:value` - `y` — The readings when the sample is measured up the y axis, or the names column of a grouped violin whose readings are on x. - Animatable - Type: `series` - `on` — Where a one-sample violin stands on the other axis — a number, a name, or a date, as a `boxplot`'s `on:`. Required on a plane for a violin written with one coordinate; refused on the number line, whose violins take lanes, and on a grouped violin, whose names column already says where. - Animatable - Type: `axis-value` Only with argand plane: - `re` — The readings on the real axis, on the argand plane. - Animatable - Type: `series` - `im` — The readings on the imaginary axis, on the argand plane. - Animatable - Type: `series` - `on` — Where a one-sample violin stands on the other axis — a number, a name, or a date, as a `boxplot`'s `on:`. Required on a plane for a violin written with one coordinate; refused on the number line, whose violins take lanes, and on a grouped violin, whose names column already says where. - Animatable - Type: `axis-value` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` ## Examples ```chalk violin(x: 2, 3, 3, 4, 4, 5, 7, 8, 8, 9) ``` ```chalk violin(x: #penguins.flipper; y: #penguins.species) ``` ```chalk violin(y: #trial.mass; on: control; bandwidth: 0.4) ``` --- # Expressions Writing maths a graph can plot, the parameters a reader can move, and the {{…}} group that computes a value anywhere in a document. ## Writing an expression Wherever a graph wants a function, it takes a math expression in `x`: `curve(expression: x^2 - 2x + 1)`, `sin(a * x)`, `100 - 4.9x^2`. Powers use `^`, the usual functions are available by name, and multiplication may be implicit — `4.9x^2`, `2(x + 1)` and `(m - px) / q` with multi-letter parameter runs all parse. > `x` is the only free variable, with one exception: the surface marks `contour` and `heatmap` plot a function of the plane, so `y` is legal alongside it there and nowhere else. ## Parameters A `parameter` declares a single-letter variable and gives the reader a slider for it. Every expression in the environment mentioning that letter re-evaluates as it moves; `Interaction` covers the control itself. ```chalk canvas.graph{ parameter#m(var: m; range: [0, 5]; default: 2) curve#f(expression: m * x) point(x: 1; y: m; label: $m$) } ``` ## Every parametised- attribute type Any attribute whose type carries `parametised-` takes an expression where a plain value would otherwise stand — a point's coordinates may be `(a, 2a)`, a curve's domain `[0, b]`, an integral's bound `from: m - s`. The `attribute types` page lists each of them and what it accepts; every reference page names the type of each of its attributes. ## The {{…}} group That is the graph's own expression language, written bare in an attribute. There is a second one, which belongs to the document rather than to the canvas: a `{{…}}` group holds an expression the *compiler* evaluates, and it may stand in any value position at all — an attribute, a header field, a line of prose. ```chalk The fitted exponent is {{round(#trend.r2 * 100, 1)}}%. curve(degree: {{k}}) point(size: {{2 * base-size}}) ``` An expression may contain numbers, the constants `pi` and `e`, references (`#id.member`), arithmetic (`+ - * / ^`, unary `-`, parentheses), and the functions below. There is **no separate text-substitution form**: `{{k}}` is the expression `k`, which is why a constant that holds text still works — a constant's text is untyped and takes the type of the position it lands in. So `@let(units: metres)` then `{{units}}` in prose is fine, and `{{units + 1}}` is an error. ## The function set The set is closed. Arity is checked when the document compiles, and so is any domain failure the compiler can see, so an expression that reaches the renderer can only fail on a value the reader is moving. - `round(x, places?)` — half away from zero. `ceil`, `floor`, `abs`. - `clamp(x, lo, hi)`, and the variadic `min(…)` and `max(…)`. - `sin cos tan asin acos atan sinh cosh tanh`, `exp`, `sqrt`. - `log` and `ln` — **both natural**. `log10` is base ten. - `erf`, `gamma`, `choose(n, k)`, `factorial(n)`. > `log` is the natural logarithm, not the base-ten one. If you want base ten, write `log10`. This is the one place where a reasonable guess gives a wrong number silently. Two spelling rules matter as much as the vocabulary. **Adjacency is a product** — `2k`, `2(x + 1)`, `(a)(b)`, `2 sin(x)` — and **a name is a maximal alphanumeric run beginning with a letter**. So `2k` is two tokens, because a literal begins with a digit, but `kx` and `k2` are single names. To multiply two named values write `k x` or `k*x`. Names are lowercased, so `T` and `t` are one name — use `period` and `t` rather than relying on the case. A name may carry a **member tail**: `row.area` is one name, not a product with a dot in it. There is exactly one thing a dotted name reads — the cell of a dataset row, inside a `@for` over that dataset — so `{{row.area * 2}}` is arithmetic on that row's cell. It is never a free name: an unresolved dotted name says either that the row has no such column, listing the ones it has, or that the name is not a row at all (which is what `{{i.area}}` under a plain `@for:3` gets). ## Free names Every name in an expression must resolve. A name that is not a function, a builtin or a constant in scope is an error at the line that wrote it — `{{2k}}` with no `k` declared reports *undefined constant `k`*, so a mistyped constant name is caught rather than silently becoming something that never arrives. > That is the rule *outside* an equation. Inside an `equation`-typed position — an attribute a library declares as a formula over named variables — a name that is neither one of those variables nor a constant in scope is a **free name**: typed number, lowercased, scoped to its template expansion like an id, and left for the renderer to supply. It is the one place an unresolved name is legal, because a formula is a rule rather than a value and its inputs arrive later. A `{{…}}` group inside an equation position is left for the equation to read as a subtree rather than resolved on its own, so the two kinds of name can sit side by side in one formula: in `{{2x}} + {{a}}` under `variables: x`, the `x` binds as the equation's variable and the `a` as a constant — or, undeclared, as a free name. No construct in this library takes an equation, so no internote written against it carries a free name. Where a graph wants a formula it takes one of the `parametised-` attribute types described above, whose letters are the graph's own and are resolved by the environment rather than by the compiler. ## Reading another construct `{{#id.member}}` reads the attribute's **typed value**, and it resolves document-wide — forwards as readily as backwards, so prose may cite a fit that appears in a later scene. A number takes part in the arithmetic around it; an enum is its canonical value; a dataset column is its cells as a list, comma-joined when it lands in text. ```chalk The line rises {{#trend.slope}} per year, or {{100 * #trend.slope}} per century. ``` Reading a `derived attribute` this way makes the expression *bound*: it cannot finish at compile time, because the value does not exist until the canvas computes it. That is not an error — the tree travels to the renderer and is evaluated there, every frame, exactly as any other bound expression is. Cycles among such expressions are errors. ## Where a bound value is refused A bound value is refused wherever the *compiler itself* has to read the result. It cannot wait for the renderer in these places, so it says so: - an `@if` pair, and `@for`'s `n` — the count, the range, or the dataset it walks; - an id — `figure#f-{{i}}` outside a loop has no `i` to read, and is an error; - a `@data` cell; - a `literal` body, and a code environment's body; - a dynamic switch's discriminant, a declaration's `default`, and a directive head. These positions also keep the in-order rule: a forward `{{#id.member}}` resolves everywhere else, but not here. A `@let` may hold a bound value — the refusal lands where it is used, not where it is written. > A value cannot introduce structure. `@let(x: - item one)` then `{{x}}` on a line of its own is a **paragraph** whose text begins with a hyphen, not a list item. Only a `@define` section declared as constructs splices structure. Inline markup in a constant still scans, so `@let(name: **bold**)` is bold where it lands. --- # canvas.code Code as a canvas environment: the full editor experience — syntax highlighting, line numbers, a line/column readout, error markers — with the code choreographed like everything else on a canvas. Content is written as `lines{}` chunks; cues wrap chunks to bring them in and out on the timeline, and the environment assembles whatever is visible into one document, numbering the lines actually shown. With `editable: true`, the reader’s first keystroke forks the assembled text into their own copy (kept between visits); choreography freezes until they reset. - Kind: Node - Section: Code environment - Writing with it: Code environment execution model ## The code environment `canvas.code` puts code on the canvas with syntax highlighting, line numbers, a line/column readout and error markers, and lets cues choreograph it like anything else there. Its implicit attribute is `language`; `title` names the environment, `editable` lets the reader edit the text, and `continue: #id` carries session state forward from an earlier environment. ```chalk canvas.code{ lines{ import numpy as np # Baltra, Bartolomé and Santa Cruz: area in km² and plant species counted area = np.array([25.09, 1.24, 903.82]) species = np.array([58, 31, 444]) log_area = np.log10(area) log_species = np.log10(species) z, log_c = np.polyfit(log_area, log_species, 1) } }( language: python ) ``` > A code environment does not execute anything by default. An output shown to the reader is authored — a comment or a later chunk asserting what an interpreter would print. A `runnable` environment is the opt-in exception: the reader may run it, into the environment's console, and nothing it prints ever reaches the canvas. ## Lines Code lives in `lines` chunks: the unit a cue wraps and the unit every other reference addresses. Chunks rather than line numbers, so that inserting a line above a chunk does not renumber what a cue points at. A chunk takes `indent` (its implicit attribute), `highlight`, and the error and diff attributes `error-lines` with `error-message`, `added-lines` and `removed-lines`. ## The typing cue `cue.type` types the chunks it wraps in character by character on the timeline. `in:` anchors when the typing starts and `speed:` is characters per second, 25 by default. ```chalk canvas.code{ lines{ z, log_c = np.polyfit(log_area, log_species, 1) } cue.type{ lines{ print(z) # ≈ 0.34 } }( in: 1 ) }( language: python ) ``` ## Splicing values into code `{{ }}` substitution reaches literal code bodies exactly as it reaches prose — it is a preprocessor over source text, so an `@let` constant or a fully-qualified dataset column splices into a chunk before anything is parsed: ```chalk @data#survey:galapagos-plants-1973.csv scene{ The areas come from the attached survey. }[ canvas.code{ lines{ area = np.array([{{#survey.area}}]) } }( language: python ) ] ``` The column arrives as its cells joined with commas, which is the form a Python list literal takes. ## Running code `runnable: true` puts a Run button in the environment's toolbar for signed-in readers. A run executes exactly what the editor shows — the reader's fork, once they have edited — in a disposable sandbox with no network access; stdout and stderr stream into a console that folds out of the environment's footer, and a runtime error marks the line it names in the editor. Running needs an explicit `language` (`auto` never runs). Python runs with the scientific stack ready — `numpy`, `pandas`, `statsmodels`, `matplotlib` — plus the standard library; JavaScript runs on Node with its built-in modules; C compiles with gcc, AddressSanitizer on, so memory errors report their line. No other packages can be installed. A chunk with `setup: true` is provisioning rather than content: it never displays, and it always executes, prepended in source order outside cue gating — so what a run computes cannot depend on where the reader has scrolled. Imports and fixture values live there. ```chalk canvas.code{ lines{ import statistics }( setup: true ) lines{ area = [25.09, 1.24, 903.82] print(statistics.mean(area)) } }( language: python runnable: true editable: true ) ``` ## Attributes - `language` — Drives the syntax highlighting, the header icon, and the display name. - Implicit - Options: `auto`, `javascript`, `typescript`, `python`, `java`, `csharp`, `ruby`, `go`, `cpp`, `php`, `swift`, `plaintext`, `html`, `css`, `json`, `xml`, `bash`, `sql`, `kotlin`, `rust`, `scala`, `dart`, `lua`, `haskell`, `elixir`, `clojure`, `erlang`, `perl`, `r`, `matlab`, `groovy`, `objective c`, `visual-basic`, `assembly`, `fortran`, `cobol`, `fsharp`, `ocaml`, `powershell`, `shell`, `yaml`, `markdown`, `graphql`, `protobuf`, `chalk`, `postgres`, `postgresql`, `sass`, `scss`, `other`, `js`, `ts`, `py`, `cs`, `c#`, `rb`, `c++`, `kt`, `rs`, `sh`, `hs`, `exs`, `clj`, `cljs`, `erl`, `pl`, `m`, `vb`, `vbs`, `asm`, `f90`, `f95`, `f03`, `f08`, `f15`, `f18`, `cob`, `cbl`, `fs`, `fsi`, `fsx`, `ml`, `mli`, `ps1`, `psm1`, `yml`, `md`, `gql`, `proto`, `pg`, `psql`, `txt`, `text`, `plain`, `objc` - Also written: `lang` - Shorthand: `canvas.code:value` - Default: `auto` - `title` — The filename shown in the header tab, beside the language’s own mark. - Type: `string` - Also written: `name`, `filename`, `file-name`, `file` - `editable` — The reader may edit the code. Editing forks the assembled text into their own copy and freezes choreography; on a runnable environment, Run executes the fork. - Type: `boolean` - Default: `false` - `runnable` — The reader may run the environment: the shown text (their fork, once edited) plus setup chunks executes in a disposable sandbox, streaming stdout and stderr into the environment's console. Needs an explicit language (auto never runs); Python (with the scientific stack), JavaScript and C. - Type: `boolean` - Default: `false` - `show-line-numbers` — Show the line-number gutter. Numbering runs over whatever the timeline has made visible, not over the chunks as written. - Type: `boolean` - Also written: `line-numbers` - Default: `true` - `wrap` — Start with soft wrap on. - Type: `boolean` - Default: `false` - `continue` — Inherit an earlier same-type environment's state: `continue: #that-environment`. - Type: `reference` ## Allowed content - Body `{ }`: `cue.type`, `lines`, `cue`, `cue.draw`, `cue.trace`, `cue.highlight`, `cue.spotlight` ## Allowed in - `scene` — detail `[ ]` ## Examples ```chalk canvas.code{ lines{ def greet(name): return f"Hello, {name}!" } }( language: python title: greet.py ) ``` ```chalk canvas.code{ lines{ x = 1 } cue{ lines{ y = x + 1 } }( in: 1 ) }( language: python ) ``` --- # lines A chunk of literal code lines inside a `canvas.code` environment — the unit cues wrap and the unit everything else addresses. Chunks rather than line numbers, because choreography shifts line numbers: the moment a cue inserts three lines, every number below it points at the wrong line. Common indentation is stripped, so the chalk source can be indented normally. - Kind: Node - Section: Code environment - Writing with it: Code environment execution model ## Attributes - `indent` — Indentation levels (four spaces each) restored on assembly — the compiler dedents every literal body to its own margin, so a chunk continuing a block states its depth. - Implicit - Type: `number` - Also written: `indent-level` - Shorthand: `lines:value` - Default: `0` - `highlight` — The continuous highlight: these lines carry a wash for as long as they are shown. For a momentary one, wrap the chunk in a `cue.highlight`. - Animatable - Type: `boolean` - Default: `false` - `error-lines` — Lines to flag with the gutter’s error marker — 1-based within THIS chunk. - Type: `index-list` - Also written: `error-line`, `errors` - `error-message` — The diagnostic the interpreter would give — rendered inline at the end of the chunk's first error line, the way an IDE annotates a broken line. - Type: `string` - Also written: `diagnostic`, `error-note` - `added-lines` — Diff wash: these lines carry the green added treatment — 1-based within THIS chunk. - Type: `index-list` - Also written: `added-line`, `added` - `removed-lines` — Diff wash: these lines carry the red removed treatment — 1-based within THIS chunk. - Type: `index-list` - Also written: `removed-line`, `removed` - `hidden` — Declared but not assembled. - Animatable - Type: `boolean` - Default: `false` - `setup` — The chunk never displays and always executes — prepended to a run in source order, outside cue gating and hidden. - Type: `boolean` - Default: `false` - `lock` — Reserved for editable-environment rules. - Type: `boolean` - Also written: `locked` - Default: `false` ## Allowed content - Body `{ }`: Literal code lines, kept verbatim. ## Allowed in - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` - `cue.type` — body `{ }` - `canvas.code` — body `{ }` ## Examples ```chalk lines{ def f(x): return x * 2 } ``` ```chalk lines{ return x * 2 }( highlight: true ) ``` ```chalk lines{ def f(x): return undefined_name }( error-lines: 2 error-message: NameError: name 'undefined_name' is not defined ) ``` ```chalk lines{ return total / len(values) }( indent: 1 added-lines: 1 ) ``` --- # canvas.geo A geographic environment on the scene's canvas. Its `in:` names the boundary pack the environment is drawn over — `canvas.geo:world` is a world map on its own, with no child node needed — and every overlay inside it inherits that pack. The body holds those overlays: a `choropleth` joined to an `@data` dataset, `marker` points, great-circle `arc` routes, further `region` layers, and the parameters that drive them. The frame is a camera rather than a pair of domains — `centre:` (latitude first) and `zoom:` (web-map doubling levels) — and both are animatable with `!cue.to`, so one keyframe on each is a fly-to. The committed packs ship with the library and an attached `.geojson` travels with the document, so nothing is ever fetched from a tile server: a map renders offline, under both themes, and identically in ten years. - Kind: Node - Section: Geographic environment - Writing with it: Selecting canvas content ## The geographic environment `canvas.geo` is a map surface. Its implicit attribute is `in`, the boundary pack the environment is drawn over, so `canvas.geo:world{ }` is already a world map with nothing inside it. The committed packs are `world` (Natural Earth 110m countries) and six admin-1 sets — `us-states`, `canada-provinces`, `australia-states`, `uk-counties`, `nz-regions` and `europe-admin1`. Every overlay in the body inherits that pack unless it names its own. `title` captions the environment, `projection` chooses how the sphere is laid flat, and `show-scale` (on by default) says what a length on screen is worth on the ground. ```chalk canvas.geo:world{ marker(at: 48.85, 2.35; label: Paris) }( title: Where the survey ran projection: equal-earth ) ``` Boundaries a pack does not carry — electorates, catchments, council wards — arrive as an attached `.geojson` file, and `in` names it the same way it names a pack: `in: #wards`, pointing at an `@data` declaration. Either way the geometry is drawn as SVG, exactly as a graph mark is, and nothing is fetched from a tile server — which is what lets a map render offline, under both themes, and identically in ten years. > The projection is a claim, not a decoration. `equal-earth` keeps areas honest and is the right world framing; `mercator` keeps angles honest and is the frame on which a great-circle route visibly bends; `conic` tunes its standard parallels to whatever the environment contains and is the right choice for one country; `albers-usa` carries Alaska and Hawaii in as insets; `globe` is the sphere itself, the far hemisphere behind the horizon. Choose the one whose distortion your claim can afford. ## centre, zoom and the fly-to A geographic environment is framed by a camera rather than by a pair of domains: `centre` — latitude first — and `zoom`, in web-map doubling levels, where `1` fits the world's width and each level halves the span. Omit them both and the environment fits its own contents across the whole choreography, so a marker that travels stays framed at every point of its journey. Both are animatable, and a `!cue.to` on each with the same anchor is the fly-to. A `centre` may also be written as `#id` naming a marker the environment already draws, so a map framed on Tokyo that also pins Tokyo states the place once; a keyframe onto a reference snaps rather than gliding, because a reference has no midpoint to interpolate through. ```chalk canvas.geo#tour:world{ region(in: world; only: France, Germany; fill: teal) }( centre: 20, 0 !cue.to{50.5, 15}(at: 1; over: 900ms) zoom: 1 !cue.to{4}(at: 1; over: 900ms) ) ``` `interactive: true` hands the camera to the reader — scroll to zoom, drag to pan, double-click to reset. Exploration stays on the environment's subject: the zoom floor is the frame that fits its packs, markers and arcs, so a reader cannot lose the map. Their camera then overrides the authored one for the session, and `continue: #that-environment` carries it into a later environment along with their parameter values. ## Overlays Everything drawn over the basemap is an overlay, and the four share `opacity` and `hidden` (declared but not drawn, and animatable, so `hidden: true !cue.to{false}(at: 2)` is a reveal). Cues wrap overlays exactly as they wrap graph marks. The attention cues do not reach this environment: a highlight ring and a spotlight are plot machinery, and a geographic environment admits only `cue` and `cue.draw`. ### Boundary layers The environment's `in:` already draws its pack, so a `region` is for what a basemap cannot say: a subset in its own colours, a second pack layered over the first, an attached`.geojson` over a committed pack, or boundaries that arrive on a cue. `only` and `except` narrow it by name or ISO alpha-3 code, matched case-insensitively — `France`, `DEU` and `Ivory Coast` all read — and `fill`, `stroke` and `width` paint what is left. ```chalk canvas.geo:world{ region(in: world; except: Antarctica) region(in: world; only: France, Germany, Italy; fill: teal) } ``` ### Joining a dataset to regions `choropleth` joins a `@data` column onto the pack's regions — the geographic sibling of the graph's `heatmap`. `regions` names the column of region names and `values` the column to shade by; join keys match pack names and short codes case-insensitively, and a region the join does not cover keeps the neutral land fill rather than disappearing. What `values` holds decides the reading. A numeric column shades through a ramp — `multicolour`, `temperature`, or a single hue's own intensity — with `range` pinning the window. A text column enumerates instead: each distinct value becomes a category with its own flat hue, named by `key`, and `intensity` gives depth within that hue. That pair is the electoral map's own grammar — the winner names the colour, the margin names how solid it is. ```chalk canvas.geo:australia-states{ choropleth( regions: #vote.state values: #vote.party intensity: #vote.margin key: Labor red, Coalition blue, Greens green ) }( title: Who led each state ) ``` > A choropleth publishes derived `min`, `max`, `mean` and `count` over the values the join actually covered, so prose can cite the spread the shading spans instead of a number typed in beside it: give the mark an id — `choropleth#gdp(…)` — and read `{{#gdp.max}}`. ### Places `marker` is a point on the earth, in two forms. Singly, `at` takes a coordinate, latitude first, with an optional `label`; either half may be a parameter expression, so a marker rides a slider or a keyframe. Data-bound, `lat` and `lon` name columns and the one node draws every row — add `size-by` and symbol *area* scales with that column, which is the proportional-symbol map. ```chalk canvas.geo:world{ marker(lat: #quakes.lat; lon: #quakes.lon; size-by: #quakes.magnitude; colour: red; opacity: 0.6) } ``` ### Great-circle routes `arc` draws the great-circle path between two places — the shortest route over the earth, drawn the way the earth actually is. On a `mercator` environment the London–Tokyo arc bends far north of the straight line between them, and that one element is the whole projection-distortion lesson. Either end may be `#id` naming a marker in the same environment, and an anchored end follows its marker. An arc publishes derived `distance` in kilometres — the number the arc is. ```chalk canvas.geo:world{ cue.draw{ arc#route(from: #london; to: #tokyo; label: {{round(#route.distance, 0)}} km) }( in: 1 in-duration: 1200ms ) marker#london(at: 51.47, -0.46; label: London) marker#tokyo(at: 35.55, 139.78; label: Tokyo) }( projection: mercator ) ``` ## Moving a map through time A map moves through time in one of two ways, and which one is a fact about the data. For a handful of periods, chain the columns: `values` is animatable, so `#y1990.rate !cue.to{#y2000.rate}(at: 1)` eases every region between its own two readings. Pin `range` when you do, or the ramp re-normalises each frame and the change erases itself. For long-format data — one row per region per period — a parameter bound `over:` the period column is the better tool. It takes its range, its stops and its format from the data rather than being told them, so naming the column gives the environment a scrubber over exactly that span; marks carrying a `period` slice themselves against it, interpolating between the two periods that bracket wherever the reader is standing. Writing it as a `cue.parameter` hands the scrubbing to the scene: the map runs its years from the anchor, and the step's own transport plays it. ```chalk canvas.geo:world{ cue.parameter#years(var: t; over: #rates.year; name: Year; at: 1; duration: 12s) choropleth(regions: #rates.country; values: #rates.rate; period: #rates.year; range: [0, 220]) } ``` `Parameters and cues` gives the rule the choice actually turns on: a change belongs to a scrubber when one sentence holds across its whole range, and to a cue otherwise. ## Derived attributes Two overlays compute something and publish it for reading: a choropleth's `min`, `max`, `mean` and `count`, and an arc's `distance`. A `parameter` publishes `value`, where the reader is standing — the period, on one bound `over:` a period column. Reach one by member access on the node's id — `#route.distance` in an attribute, `{{#route.distance}}` in prose. `Derived attributes` covers how they behave. ## Attributes - `in` — The environment's basemap — the boundary pack it draws, and the one every overlay inside it inherits. `world` is Natural Earth 110m countries; the other six are admin-1 packs. It may instead be `#id` naming an attached `.geojson` dataset — `in: #wards` — which makes the attachment itself the boundaries. Normally written in the head, `canvas.geo:world`, the way `canvas.code:python` is. - Implicit - Options: `world`, `us-states`, `canada-provinces`, `australia-states`, `uk-counties`, `nz-regions`, `europe-admin1` - Type: `reference` - Also written: `basemap` - Shorthand: `canvas.geo:value` - `names` — For an attached `.geojson` only: the feature property holding each region's name, which is what a `choropleth` joins on. Absent, the first of `name`, `NAME`, `title` or `id` the features carry. - Type: `string` - `codes` — For an attached `.geojson` only: a feature property holding a short join code — the custom equivalent of a pack region's ISO alpha-3. - Type: `string` - `simplify` — For an attached `.geojson` only: Douglas–Peucker tolerance in degrees, thinning boundary detail as the file is read. `0` keeps every point; a large file wants about `0.01`. - Type: `number` - Default: `0` - `title` — The caption set over the top-left of the environment. A map carries no axis labels to name its own subject, so unlike a graph's `title:` this one is visible content and not merely an accessible name. Rich text, so `$maths$` and inline formatting render. - Type: `rich-text` - `projection` — How the sphere is laid flat — which decides what the map tells the truth about. `equal-earth`, the default, keeps AREAS honest and is the right world framing. `mercator` keeps ANGLES honest: the frame every slippy map uses, and the one on which a great-circle `arc` visibly bends away from the straight line. `equirectangular` plots latitude and longitude straight. `conic` is an Albers equal-area conic that tunes its standard parallels to whatever the environment contains — what national atlases use, and the right choice for a single country or region. `albers-usa` is the United States composite, the lower 48 on an equal-area conic with Alaska and Hawaii carried in as insets, so a US environment spends no width on the Pacific. `globe` is the sphere itself, orthographic, with the far hemisphere behind the horizon. - Options: `equal-earth`, `mercator`, `equirectangular`, `conic`, `albers-usa`, `globe` - Default: `equal-earth` - `centre` — Where the camera looks: a coordinate written LATITUDE FIRST — `centre: 48.85, 2.35` — or `#id` naming a marker this environment already draws, `centre: #tokyo`, so the place is stated once. Either half of a coordinate may be a parametised expression. Animatable: `centre: 20, 0 !cue.to{50.5, 15}(at: 2; over: 900ms)` pans the frame, and writing the same anchor on `zoom:` makes the two move as one fly-to. A keyframe targeting a `#id` snaps rather than gliding, because a reference has no midpoint to interpolate through. Omitted, the environment frames its own contents. - Animatable - Type: `geo-anchor` - Also written: `center` - `zoom` — Web-map doubling `levels:` `1` fits the world's width, and each level halves the span. Omitted, the zoom is AUTOMATIC — the environment fits what is inside it across the whole choreography, so a marker that travels stays framed at every point of its journey; writing `zoom: 1` asks for exactly 1 instead. Parametised, and animatable with `!cue.to`. - Animatable - Type: `parametised-number` - `interactive` — Let the reader explore: scroll to zoom about the cursor, drag to pan, double-click to reset, with zoom and reset controls in the environment's toolbar. Exploration stays on the environment's subject — the zoom floor is the frame that fits its packs, markers and arcs, and panning cannot leave them behind. Once touched, the explored camera overrides the authored one for the rest of the session. - Type: `boolean` - Default: `false` - `show-scale` — The scale bar in the corner, saying what a length on the screen is worth on the ground. On by default, because every projection here changes scale across the frame and a reader has no other way to judge distance. It is measured at the middle of the environment, the only place the reading is honest. Turn it off for an environment where distance is beside the point. - Animatable - Type: `boolean` - Also written: `scale` - Default: `true` - `show-graticule` — Draw the latitude and longitude grid over the environment. Animatable, so a cue can bring the grid in at the step where the projection itself becomes the subject. - Animatable - Type: `boolean` - Also written: `graticule` - Default: `false` - `graticule-spacing` — Degrees between graticule lines. Does nothing without `show-graticule:`. - Type: `number` - Default: `15` - `continue` — Inherit an earlier geographic environment's state: `continue: #that-environment`. The reader's explored camera and their parameter values carry forward; every authored attribute is the new environment's own. - Type: `reference` ## Allowed content - Body `{ }`: `region`, `choropleth`, `marker`, `arc`, `parameter`, `cue.parameter`, `cue`, `cue.draw` ## Allowed in - `scene` — detail `[ ]` ## Examples ```chalk canvas.geo:world(title: The world) ``` ```chalk canvas.geo:australia-states{ choropleth(regions: #vote.state; values: #vote.party; key: Labor red, Coalition blue) }( title: Who led each state ) ``` ```chalk canvas.geo#tour:world{ region(in: world; only: France, Germany; fill: teal) }( centre: 20, 0 !cue.to{50.5, 15}(at: 1; over: 900ms) zoom: 1 !cue.to{4}(at: 1; over: 900ms) ) ``` ```chalk canvas.geo:canada-provinces{ choropleth(regions: #d.province; values: #d.rate) }( projection: conic ) ``` ## Notes - An environment's own id is written on the node — `canvas.geo#europe{…}` — and `continue:` points back at it with `#europe`. - Cues wrap overlays exactly as they wrap graph marks, and `cue.draw` traces an `arc` along its length. - The cursor's coordinates, and the name of the region beneath it, read out in the corner of the environment. --- # arc The great-circle path between two places — the shortest route over the earth, drawn the way the earth actually is. On a `mercator` environment the London–Tokyo arc bends far north of the straight line between them, and that one element is the whole projection-distortion lesson. Wrap it in a `cue.draw` and the route traces itself along its length. - Kind: Node - Section: Geographic environment › Marks ## Attributes - `from` — One end: a coordinate written latitude first, or `#id` naming a marker in the same environment — `from: #london`. An anchored end FOLLOWS its marker, so an arc drawn to an animated marker moves with it. Parametised and animatable. - Required, Implicit, Animatable - Type: `geo-anchor` - Shorthand: `arc:value` - `to` — The other end, in the same two forms. - Required, Animatable - Type: `geo-anchor` - `colour` — The arc's hue. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `teal` - `width` — Stroke width in pixels. - Animatable - Type: `number` - Default: `2` - `opacity` — How solid the stroke is, `0` to `1`. - Animatable - Type: `number` - Default: `1` - `label` — Rich inline label set at the arc's midpoint. - Type: `rich-text` - `hidden` — Declared but not drawn. Animatable, so `hidden: true !cue.to{false}(at: 2)` is the overlay's reveal. - Animatable - Type: `boolean` - Default: `false` ## Derived attributes Computed while the element renders — read one with `#id.name` in attribute position or `{{#id.name}}` in prose, never set. - `distance` — The great-circle length in kilometres — the number the arc IS, the way an `integral` derives its `value`. Cite it in prose with `{{#id.distance}}`. - Derived ## Allowed in - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` - `canvas.geo` — body `{ }` ## Examples ```chalk arc(from: #london; to: #tokyo; colour: teal) ``` ```chalk arc#route(from: 51.47, -0.46; to: 35.55, 139.78; label: {{round(#route.distance, 0)}} km) ``` ```chalk cue.draw{ arc(from: 40.64, -73.78; to: 1.36, 103.99) }( in: 2 in-duration: 1200ms ) ``` --- # choropleth A dataset column joined onto a pack's regions and shaded by value — the geographic sibling of the graph's `heatmap`. `regions:` and `values:` name `@data` columns (`#gdp.country`, `#gdp.gdppc`); join keys match pack names and short codes — ISO alpha-3 on `world`, postal abbreviations on the admin-1 packs — case-insensitively, and any region the join does not cover keeps the neutral land fill. A NUMERIC `values:` column shades through a ramp, with a legend row pricing whichever region is under the cursor. A TEXT column enumerates instead: each distinct value becomes a category with its own flat hue, named by `key:`, and the legend lists the swatches. - Kind: Node - Section: Geographic environment › Marks ## Attributes - `in` — The boundary pack the join shades. Defaults to the environment's own `in:`. It may instead be `#id` naming an attached `.geojson` dataset — `in: #wards` — which makes the attachment itself the boundaries. - Implicit - Options: `world`, `us-states`, `canada-provinces`, `australia-states`, `uk-counties`, `nz-regions`, `europe-admin1` - Type: `reference` - Shorthand: `choropleth:value` - `regions` — The `@data` column holding the region `names:` `regions: #gdp.country`. - Required - Type: `reference` - `values` — The column to shade by: `values: #gdp.gdppc`. Numeric shades through the ramp; text enumerates categories instead (`values: #vote.party`). ANIMATABLE, which is how a map moves through time: a chain of columns — `values: #y1990.rate !cue.to{#y2000.rate}(at: 1)` — eases every region between its own two readings. Pin `range:` when animating, or the ramp re-normalises each frame and the change erases itself. Every reference is checked at compile time — the base and each keyframe, dataset and column alike — so a mistyped column fails the build rather than shading nothing. - Required, Animatable - Type: `reference` - `colour` — `multicolour`, the default, runs the full spectrum, violet low to red high; `temperature` is the blue → white → red ramp for diverging data — pair it with a symmetric `range:` to pin white at zero; any ordinary hue shades its own intensity ramp instead. Both ramps are fixed colours, identical in light and dark mode. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black`, `multicolour`, `temperature` - Also written: `color` - Default: `multicolour` - `range` — The value window the ramp spans, `range: [0, 60]`; values outside it clamp. Absent, the ramp spans the joined extrema. Animatable — a `!cue.to` re-normalises the shading across steps. - Animatable - Type: `parametised-interval` - `invert` — Flip the ramp, putting deep colour at low values rather than high. - Animatable - Type: `boolean` - Default: `false` - `key` — The categorical legend, category then hue: `key: Liberal red, Conservative blue, New Democratic orange`. Categories the list does not name take the next unused hue, so nothing in the data silently vanishes. Ignored when `values:` is numeric. - Type: `category-colours` - Also written: `legend` - `period` — The period column of a LONG-FORMAT table — one row per region per period. The mark then shows the slice at whatever the environment's parameter over that column holds, interpolating between the two periods that bracket it, so a reader scrubbing to 1995 sees half way between the 1990 and 2000 rows. Reach for this rather than a keyframe chain on `values:` once the data carries more than a few periods. - Type: `reference` - Also written: `at` - `intensity` — Depth WITHIN a category's hue — the electoral map's own grammar: the winner names the colour, the margin names how solid it is, so a knife-edge win is pale and a landslide is full. A numeric column, written beside a categorical `values:`. `range:` pins its window; absent, the observed extrema span it. - Type: `reference` - Also written: `depth`, `by` - `opacity` — How heavy the shading sits over the basemap, `0` to `1`. - Animatable - Type: `number` - Default: `1` - `hidden` — Declared but not drawn. Animatable, so `hidden: true !cue.to{false}(at: 2)` is the overlay's reveal. - Animatable - Type: `boolean` - Default: `false` ## Derived attributes Computed while the element renders — read one with `#id.name` in attribute position or `{{#id.name}}` in prose, never set. - `min` — The smallest joined value. Cite it in prose with `{{#id.min}}`. - Derived - `max` — The largest joined value. - Derived - `mean` — The mean of the joined values. - Derived - `count` — How many regions the join actually covered — worth citing when it is fewer than the dataset has rows. For a categorical `values:` column it is the only aggregate published: min, max and mean need numbers. - Derived ## Allowed in - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` - `canvas.geo` — body `{ }` ## Examples ```chalk choropleth#gdp(in: world; regions: #gdp.country; values: #gdp.gdppc) ``` ```chalk choropleth(in: world; regions: #anomaly.country; values: #anomaly.delta; colour: temperature; range: [-2, 2]) ``` ```chalk choropleth#winner(in: canada-provinces; regions: #vote.province; values: #vote.party; key: Liberal red, Conservative blue, New Democratic orange) ``` ```chalk choropleth(regions: #vote.state; values: #vote.party; intensity: #vote.margin; key: Greens green, Labor red) ``` ```chalk choropleth(regions: #y1990.country; values: #y1990.rate !cue.to{#y2000.rate}(at: 1; over: 1.6s); range: [0, 220]) ``` ```chalk choropleth(regions: #rates.country; values: #rates.rate; period: #rates.year; range: [0, 220]) ``` --- # marker A point on the earth. Singly, `at:` takes a coordinate written latitude first, with an optional `label:` — parametised and animatable, so a marker can ride a slider or a keyframe. Data-bound, `lat:` and `lon:` name `@data` columns and the one node draws every row; add `size-by:` and symbol AREA scales with that column, `size:` giving the largest symbol's radius — the proportional-symbol map. - Kind: Node - Section: Geographic environment › Marks ## Attributes - `at` — The place, latitude first: `at: 35.68, 139.69`. Either half may be a parametised expression, and the pair is animatable with `!cue.to`. Omit it and give `lat:` and `lon:` instead for the data-bound form. - Implicit, Animatable - Type: `geo-coordinate` - Shorthand: `marker:value` - `lat` — Data-bound form: the `@data` column of latitudes, `lat: #quakes.lat`. One symbol is drawn per row. - Type: `reference` - `lon` — Data-bound form: the column of longitudes, `lon: #quakes.lon`. - Type: `reference` - Also written: `lng` - `size-by` — A numeric column that scales symbol AREA, not radius — which is what makes the sizes comparable by eye. Rows with no positive value draw nothing. - Type: `reference` - `size` — The symbol radius in pixels — the LARGEST symbol's, when `size-by:` scales the rest. - Animatable - Type: `parametised-number` - Default: `6` - `colour` — The symbol's hue. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `orange` - `opacity` — How solid the symbols are, `0` to `1`. Worth lowering when data-bound markers crowd each other. - Animatable - Type: `number` - Default: `1` - `label` — Rich inline label set above the marker. Single form only — a data-bound `marker` draws one symbol per row, and there is no one place to put the text. - Type: `rich-text` - `hidden` — Declared but not drawn. Animatable, so `hidden: true !cue.to{false}(at: 2)` is the overlay's reveal. - Animatable - Type: `boolean` - Default: `false` ## Allowed in - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` - `canvas.geo` — body `{ }` ## Examples ```chalk marker(at: 51.47, -0.46; label: London) ``` ```chalk marker(lat: #quakes.lat; lon: #quakes.lon; size-by: #quakes.magnitude; colour: red; opacity: 0.6) ``` --- # region An extra layer of boundaries over the environment's own basemap. The environment's `in:` already draws its pack, so a `region` is for what a basemap cannot say: a subset picked out in its own colours (`only: France, Germany` with a teal fill), a second pack layered over the first (`us-states` borders over a `world` map), or boundaries that arrive on a cue. - Kind: Node - Section: Geographic environment › Marks ## Attributes - `in` — The boundary pack this layer draws. Defaults to the environment's own `in:`. It may instead be `#id` naming an attached `.geojson` dataset — `in: #wards` — which makes the attachment itself the boundaries. - Implicit - Options: `world`, `us-states`, `canada-provinces`, `australia-states`, `uk-counties`, `nz-regions`, `europe-admin1` - Type: `reference` - Shorthand: `region:value` - `only` — Draw only these regions. Names or ISO alpha-3 codes, matched case-insensitively — `France`, `DEU` and `Ivory Coast` all read. - Type: `string-list` - `except` — Drop these regions from the layer. `except: Antarctica` is the usual world framing. - Type: `string-list` - `fill` — A hue; the land fills with its tint. Absent, the land keeps the neutral basemap grey. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - `stroke` — A hue for the borders. Absent, they are drawn as seams of the page background. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - `width` — Border width in pixels. - Animatable - Type: `number` - Default: `1` - `opacity` — How solid the layer sits over the basemap, `0` to `1`. - Animatable - Type: `number` - Default: `1` - `hidden` — Declared but not drawn. Animatable, so `hidden: true !cue.to{false}(at: 2)` is the overlay's reveal. - Animatable - Type: `boolean` - Default: `false` ## Allowed in - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` - `canvas.geo` — body `{ }` ## Examples ```chalk region(in: world; only: France, Germany, Italy; fill: teal) ``` ```chalk region(in: us-states; stroke: grey) ``` ```chalk cue{ region(in: canada-provinces; fill: orange) }( in: 2 ) ``` --- # canvas.diagram A diagram environment on the scene's canvas: an uploaded image annotated with pins, a flowchart, a simple tree. Its `image:` names an uploaded asset by file name — `canvas.diagram:cell.jpg` is an annotated image with no child node needed — and a flowchart environment simply omits it. Boxes are arranged by the environment, layered along `flow:`, so a flowchart is written as what connects to what; pins carry their own coordinates. Positions here are fractions of the content frame with the origin at the TOP LEFT and y running DOWN — `0.62, 0.31` is 62% across, 31% from the top, the way a position is read off an image — unlike the graph's y-up mathematics. The frame is a camera: `focus:` (a point, or `#id` of a box or pin) and `zoom:` (a multiplier over the fitted frame) are both animatable with `!cue.to`, so one keyframe on each glides into a feature as the narrative reaches it. - Kind: Node - Section: Diagram environment - Status: Beta - Writing with it: Selecting canvas content ## The diagram environment `canvas.diagram` holds the pictures that are neither plots nor maps: an uploaded image annotated with pins, a flowchart, a simple tree. Its implicit attribute is `image`, an uploaded asset named by its file name — `canvas.diagram:cell.jpg` is an annotated image with no child node needed — and a flowchart environment simply omits it. The environment compiles whether or not the asset is there: an `image:` naming nothing renders a message in place of the picture, saying which file name it looked for. A diagram is therefore written and compiled against the coordinates first, and the picture uploaded after. Positions are fractions of the content frame with the origin at the *top left* and y running *down*: `0.62, 0.31` is 62% across and 31% from the top, the way a position is read off an image. This is deliberately not the graph's y-up mathematics — a free-form coordinate drawing belongs in `canvas.graph` with `!show background`. ## Images and pins A `pin` is a labelled point on the image: a dot on the feature, a short hairline leader, the label beside it. `at:` is its implicit attribute; `side:` places the label, defaulting to whichever side has room. Cues gate pins exactly as they gate graph marks, so annotations arrive with the narrative. ```chalk canvas.diagram:cell-micrograph.jpg{ pin#nucleus(at: 0.62, 0.31; label: Nucleus) cue{ pin(at: 0.28, 0.55; label: Mitochondrion) }( in: 1 ) } ``` ## Boxes and arrows A flowchart is written as what connects to what, never as coordinates. `box` declares the things and `arrow` the relations between them — endpoints are `#id`s of boxes or pins in the same environment — and the environment arranges the result in layers along `flow:`, `down` by default or `right`. A tree is the same arrangement with the arrows all pointing one way. Chains that share no arrow are arranged apart, each ranked on its own, and stand side by side across the flow — two columns under `down`, two rows under `right` — so a wide box in one never moves a box in the other. A run longer than the environment has room for wraps rather than shrinking: it continues on a further line, reading back the other way, and each line begins where the last ended, so the arrow across the fold is a straight one. A rank too wide to share a line stands alone without turning the run, and a series of them is simply a column. So a box is always the size its text needs — a chain of fourteen steps sets the same type as a chain of three, on more lines. The arrows are routed through the space between the layers, between the lines and down the sides, never across a box, and each label sits on its own run. ```chalk canvas.diagram{ box#sample:terminal{Sample collected} box#pcr{ PCR amplify 30 cycles } box#check:decision{Enough DNA?} arrow(from: #sample; to: #pcr) arrow(from: #pcr; to: #check) arrow(from: #check; to: #pcr; label: no; dashed) }( flow: down ) ``` > The arrangement is computed over the whole environment, cues included: a cue controls when a box is *seen*, never where it sits, so nothing shifts as steps fire. A `hidden:` box keeps its place for the same reason. `cue.draw` strokes an arrow in along its own length; a `dashed:` arrow fades instead, since a dash pattern and the draw reveal cannot share one stroke. ### Shapes `shape:` carries the flowchart conventions a reader already knows: `box` (the default rounded rectangle), `decision` (the diamond), `terminal` (the pill) and `ellipse`. It is the implicit attribute, so `box#check:decision{Enough DNA?}` reads as the diamond it draws. A box's text is its body, rich inline — maths, bold and `{{…}}` all work — and the body keeps its source lines: the first is the label, every further line a smaller muted line beneath it. Text wider than the widest box wraps rather than spilling. ### Placing a box by hand A box with `at:` sits out of the arrangement at that fraction of the frame — a legend beside a flowchart, a caption box on an image. Everything else still arranges around it. ## focus, zoom and the glide The frame is a camera. `focus:` takes a fractional point or `#id` naming a box or pin the environment already draws, and `zoom:` multiplies over the fitted frame — omitted, the content fits the environment. Both are animatable, so one `!cue.to` on each glides into a feature as the narrative reaches it; `interactive` additionally hands the reader wheel-zoom and drag, with a double-click back to the authored frame. ```chalk canvas.diagram:cell-micrograph.jpg{ pin#nucleus(at: 0.62, 0.31; label: Nucleus) }( focus: 0.5, 0.5 !cue.to{#nucleus}(at: 1; over: 800ms) zoom: 1 !cue.to{3}(at: 1; over: 800ms) ) ``` ## Attributes - `image` — The uploaded image the environment draws under its pins, named by its file name exactly as the block `image` names one. Normally written in the head, `canvas.diagram:cell.jpg`. Omitted, the environment's frame is the arranged extent of its boxes. - Implicit - Type: `string` - Also written: `src`, `source` - Shorthand: `canvas.diagram:value` - `flow` — Which way the arrangement runs: `down` stacks layers top to bottom, `right` runs them left to right. Trees and flowcharts are the same arrangement read in different directions. Either way the arrangement wraps rather than shrinking: a run longer than the environment continues on a further line, reading back the other way, so boxes keep the size their text needs. - Options: `down`, `right` - Default: `down` - `title` — The caption set over the top-left of the environment, on the environment's own ground. - Type: `rich-text` - `focus` — Where the camera looks: a fractional point, or `#id` naming a box or pin this environment already draws — so a frame aimed at a feature it also labels states that feature once. Animatable with `!cue.to`; absent, the frame centres. - Animatable - Type: `diagram-anchor` - `zoom` — A multiplier over the fitted frame, animatable with `!cue.to`. NO default: an omitted zoom is auto — the content fits the environment — and `zoom: 1` written out means exactly the fit. - Animatable - Type: `parametised-number` - `interactive` — Reader exploration: wheel-zoom about the cursor, drag to pan, double-click to reset. The explored camera overrides the authored or keyframed one once touched. - Type: `boolean` - Default: `false` - `continue` — Inherit an earlier diagram environment's state: `continue: #that-environment`. The reader's explored camera carries forward; every authored attribute is the new environment's own. - Type: `reference` ## Allowed content - Body `{ }`: `box`, `arrow`, `pin`, `cue`, `cue.draw`, `cue.highlight`, `cue.spotlight` ## Allowed in - `scene` — detail `[ ]` ## Examples ```chalk canvas.diagram:cell-micrograph.jpg{ pin#nucleus(at: 0.62, 0.31; label: Nucleus) pin(at: 0.28, 0.55; label: Mitochondrion) }( focus: 0.5, 0.5 !cue.to{#nucleus}(at: 1; over: 800ms) zoom: 1 !cue.to{3}(at: 1; over: 800ms) ) ``` ```chalk canvas.diagram{ box#sample:terminal{Sample collected} box#pcr{ PCR amplify 30 cycles } box#check:decision{Enough DNA?} arrow(from: #sample; to: #pcr) arrow(from: #pcr; to: #check) arrow(from: #check; to: #pcr; label: no) }( flow: down ) ``` --- # arrow A relation between two named things: `arrow(from: #dna; to: #rna; label: transcription)`. The endpoints are `#id`s of boxes or pins in the same environment — an id neither declares is a compile error — and the environment routes the path: forward with the flow as an easing curve, sideways between neighbours, backward as a bow out past the boxes' flank. `cue.draw` strokes it in along its own length. A `dashed:` arrow fades under `cue.draw` instead — a dash pattern and the draw reveal cannot share one stroke. - Kind: Node - Section: Diagram environment › Marks - Status: Beta ## Attributes - `from` — `#id` of the box or pin the arrow leaves. - Required, Implicit - Type: `diagram-ref` - Shorthand: `arrow:value` - `to` — `#id` of the box or pin the arrow enters. - Required - Type: `diagram-ref` - `label` — Rich inline text set at the path's midpoint. - Type: `rich-text` - `colour` — The stroke's hue; unset, a neutral grey. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - `dashed` — A dashed stroke — the conventional weaker or conditional relation. - Type: `boolean` - Default: `false` - `hidden` — Declared but not drawn. Animatable. - Animatable - Type: `boolean` - Default: `false` ## Allowed in - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` - `canvas.diagram` — body `{ }` ## Examples ```chalk arrow(from: #dna; to: #rna; label: transcription) ``` ```chalk cue.draw{ arrow(from: #check; to: #pcr; label: no) }( in: 2 ) ``` --- # box A thing in the diagram's arrangement. Its text is its body, rich inline — maths, bold and live `{{…}}` values all work — kept line by line: the first line is the label, every further line a smaller muted line beneath it. Text longer than the widest box wraps. `shape:` is the implicit attribute and carries the flowchart conventions: `box` (the default rounded rectangle), `decision` (the diamond), `terminal` (the pill), `ellipse`. With no `at:` the environment places it, layered along `flow:` by what its arrows connect; with `at:` it sits out of the arrangement at that fraction of the frame. - Kind: Node - Section: Diagram environment › Marks - Status: Beta ## Attributes - `shape` — `box` | `decision` | `terminal` | `ellipse`. The diamond and the pill are the flowchart conventions a reader already knows. - Implicit - Options: `box`, `decision`, `terminal`, `ellipse` - Shorthand: `box:value` - Default: `box` - `at` — Out of the arrangement: the box's centre as fractions of the frame, top-left origin, y down. Boxes without it are placed by the environment. - Animatable - Type: `diagram-point` - `colour` — The box fills with the hue's own tint and strokes deeper; unset, it sits quietly on the environment's ground. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - `hidden` — Declared but not drawn — the box keeps its place in the arrangement, so a later reveal moves nothing. Animatable. - Animatable - Type: `boolean` - Default: `false` ## Allowed content - Body `{ }`: The box's text, rich inline; one source line per line of the box — the first is the label, the rest sit smaller beneath it ## Allowed in - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` - `canvas.diagram` — body `{ }` ## Examples ```chalk box#pcr{ PCR amplify 30 cycles } ``` ```chalk box#check:decision{Enough DNA?}(colour: orange) ``` ```chalk box#legend{Key}(at: 0.85, 0.1) ``` --- # pin A labelled point on the image: a dot on the feature, a short hairline leader, the label beside it. `at:` is the implicit attribute — fractions of the content frame, top-left origin, y DOWN, so `0.62, 0.31` is 62% across and 31% from the top, the way the numbers are read off an image editor. `side:` places the label; `auto` (the default) picks whichever side has room. A pin keeps its screen size whatever the camera's zoom, the way a map marker does. - Kind: Node - Section: Diagram environment › Marks - Status: Beta ## Attributes - `at` — The dot, as fractions of the frame: `0.62, 0.31`. Animatable with `!cue.to`. - Required, Implicit, Animatable - Type: `diagram-point` - Shorthand: `pin:value` - `label` — Rich inline text beside the dot, at the leader's end. - Type: `rich-text` - `side` — `auto` | `left` | `right` | `above` | `below` — where the label sits relative to the dot. - Options: `auto`, `left`, `right`, `above`, `below` - Default: `auto` - `colour` — The dot's hue; the leader follows it. - Animatable - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - `hidden` — Declared but not drawn. Animatable, so it is the reveal for annotations that arrive with the narrative. - Animatable - Type: `boolean` - Default: `false` ## Allowed in - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` - `canvas.diagram` — body `{ }` ## Examples ```chalk pin(at: 0.62, 0.31; label: Nucleus) ``` ```chalk pin#er(at: 0.44, 0.72; label: Endoplasmic reticulum; side: below; colour: teal) ``` --- # Motion The clock a scene keeps, the two kinds of cue anchored to it, and the durations and easings they take. ## The timeline A scene has a timeline, and `step` markers (`===` in the narrative) carve it into steps. Everything animated anchors to that clock: a step index (`2`), an absolute scene time (`1500ms`), or a step with an offset (`2 + 500ms`). The scene's `duration`, `autoplay` and `loop` attributes govern playback. The clock only runs as far as the markers go: an anchor past the last marker never arrives, and a marker nothing anchors to advances a canvas that does not move. `Prose and canvas state` covers auditing the two against each other. Cues take two forms, distinguished by where they are written: six wrap the content they act on, two are written inside an attribute's value. Both anchor the same way and take the same `easing`. ## Wrapping cues - `cue` — fades its content in at `in:` and out at `out:`. - `cue.draw` — draws it instead of fading: strokes draw along their own length, fills arrive as the stroke completes. - `cue.trace` — moves its content along a referenced curve: `cue.trace{ point(x: 0; y: 0) }(path: #flight; at: 2)`. - `cue.highlight` — pushes two rings out of the mark's edge, without changing whether it is there. - `cue.spotlight` — dims the rest of the environment and leaves its content lit. - `cue.type` — types code in, character by character. ```chalk canvas.graph{ scatter#points(x: 1, 2, 3; y: 2.0, 2.4, 3.1) cue{ fit(of: #points; model: polynomial; degree: 1) }( in: 1 in-duration: 800ms ) } ``` ## Keyframe cues A cue may move a value instead of wrapping content. It is written inside the attribute's value, and what is written there is the base state. ### !cue.to Each `!cue.to` names the value to move to and when. ```chalk canvas.graph{ curve(expression: 100 - 4.9x^2) }( x-domain: [0, 5] !cue.to{[0, 10]}(at: 2) ) ``` They chain left to right on one attribute — `zoom: 5 !cue.to{6.5}(at: 2) !cue.to{8}(at: 4 + 1.5s; over: 800ms)` — and the animatable surface is deliberately narrow: domains, grid spacing, the `show-` switches, mark coordinates, `hidden` (a reveal that still frames the plot) and colours. Every reference page marks the attributes in it with an *Animatable* badge. Anything else is refused rather than ignored: ```chalk canvas.graph{ curve(expression: x^2) }( x-axis-label: time !cue.to{distance}(at: 2) ) ``` Rejected by the compiler: `[attribute]` node `canvas.graph`: attribute `x-axis-label`: unknown micronode `cue.to` `!cue.zoom` is the same move for a graph's domains, with an announcement first: a box is drawn around the smaller of the two windows and held, and only then does the view move — into the box when zooming in, out of it when zooming out. The box never moves in data coordinates. `draw:` and `hold:` time it, `colour:` paints it, and one chain may mix the two forms. ```chalk x-domain: [0, 10] !cue.zoom{[2, 4]}(at: 2; draw: 400ms; hold: 300ms; colour: blue) ``` ## easing, in-duration, out-duration and over Every transition takes an `easing` — `smooth` by default, `linear` for constant speed — and a duration: `in-duration` / `out-duration` on cues (500ms for fades, 1500ms for draws), `over:` on the micronodes — 1500ms for `!cue.to`, and 1200ms for `!cue.zoom`, whose box has already said where the frame is going (its `draw:` defaults to 700ms and its `hold:` to 250ms). Durations are written in milliseconds, with `ms` and `s` suffixes accepted where a time is written; by convention a duration of a second or more is written in seconds — `1.4s`, `2s` — and anything shorter in milliseconds, so a scene's timings compare down a column (`Source style`). > A wrapping cue changes whether something is *there*; a cue on a value changes what that value *is*. Which one a change should be is decided in `Parameters and cues`. --- # cue Presence on the timeline: what it wraps fades in at its `in:` anchor and out at its `out:` anchor. The rest of the cue family nests inside it — a highlight or a trace acts on content a cue has already brought in. - Kind: Node - Section: Animation & interaction › Motion › Wrapping cues - Writing with it: Parameters & cues ## Attributes - `in` — Anchor: a step index (1), a time (1.5s), or a compound offset (1 + 500ms). Reveals at this anchor. - Implicit - Type: `optional-cue-anchor` - Shorthand: `cue:value` - Default: `none` - `out` — Anchor: a step index (1), a time (1.5s), or a compound offset (1 + 500ms). Hides at this anchor. - Type: `optional-cue-anchor` - Default: `none` - `in-duration` — In animation duration (ms). - Type: `duration` - Default: `500ms` - `out-duration` — Out animation duration (ms). - Type: `duration` - Default: `500ms` - `easing` — Easing function. - Options: `linear`, `smooth` - Default: `smooth` ## Allowed content - Body `{ }`: `cue.highlight`, `cue.spotlight`, `cue.trace`, `paragraph`, `h1`, `h2`, `h3`, `h4`, `list`, `image`, `equation`, `line`, `polygon`, `curve`, `circle`, `square`, `rectangle`, `triangle`, `star`, `diamond`, `hexagon`, `vector`, `segment`, `brace`, `angle`, `point`, `text`, `axis.label`, `axis.brace`, `axis.band`, `scatter`, `histogram`, `bar`, `lollipop`, `boxplot`, `density`, `violin`, `fit`, `parameter`, `cue.parameter`, `integral`, `residuals`, `contour`, `heatmap`, `cue.type`, `lines`, `region`, `choropleth`, `marker`, `arc`, `box`, `arrow`, `pin` ## Allowed in - `scene` — detail `[ ]` - `canvas.graph` — body `{ }` - `canvas.code` — body `{ }` - `canvas.geo` — body `{ }` - `canvas.diagram` — body `{ }` ## Examples ```chalk cue{ point(x: 1; y: 2) }( in: 1 out: 3 ) ``` ```chalk cue{ Reveal this text }( in: 2 ) ``` --- # cue.draw Presence, drawn rather than faded: strokes are drawn along their own length and fills arrive as the stroke completes. Anything with no stroke to draw fades instead. - Kind: Node - Section: Animation & interaction › Motion › Wrapping cues ## Attributes - `in` — Anchor: a step index (1), a time (1.5s), or a compound offset (1 + 500ms). Reveals at this anchor. - Implicit - Type: `optional-cue-anchor` - Shorthand: `cue.draw:value` - Default: `none` - `out` — Anchor: a step index (1), a time (1.5s), or a compound offset (1 + 500ms). Hides at this anchor. - Type: `optional-cue-anchor` - Default: `none` - `in-duration` — How long the drawing takes. - Type: `duration` - Default: `1500ms` - `out-duration` — How long the undrawing takes. - Type: `duration` - Default: `1500ms` - `easing` — Easing function. - Options: `linear`, `smooth` - Default: `smooth` ## Allowed content - Body `{ }`: `cue.highlight`, `cue.spotlight`, `cue.trace`, `equation`, `paragraph`, `h1`, `h2`, `h3`, `h4`, `line`, `polygon`, `curve`, `circle`, `square`, `rectangle`, `triangle`, `star`, `diamond`, `hexagon`, `vector`, `segment`, `brace`, `angle`, `point`, `text`, `axis.label`, `axis.brace`, `axis.band`, `scatter`, `histogram`, `bar`, `lollipop`, `boxplot`, `density`, `violin`, `fit`, `parameter`, `cue.parameter`, `integral`, `residuals`, `contour`, `heatmap`, `cue.type`, `lines`, `region`, `choropleth`, `marker`, `arc`, `box`, `arrow`, `pin` ## Allowed in - `scene` — detail `[ ]` - `canvas.graph` — body `{ }` - `canvas.code` — body `{ }` - `canvas.geo` — body `{ }` - `canvas.diagram` — body `{ }` ## Examples ```chalk cue.draw{ curve(expression: x^2) }( in: 1 ) ``` ```chalk cue.draw{ line(x: 0, 2; y: 0, 2) }( in: 1 in-duration: 900ms ) ``` --- # cue.highlight Draws attention to what it wraps: rings push out of the mark's own edge and fade as they grow, twice, in the mark's own colour — the same gesture the interface uses to ring a focused field. Presence is untouched, so it can sit inside a cue or a `cue.draw` and ring content that cue has already brought in. - Kind: Node - Section: Animation & interaction › Motion › Wrapping cues ## Attributes - `at` — When the rings start: a step index (1), a time (1.5s), or a step with an offset (1 + 500ms). - Implicit - Type: `cue-anchor` - Shorthand: `cue.highlight:value` - Default: `0` - `duration` — How long one ring takes. Two of them go, the second setting off while the first is still on its way out. - Type: `duration` - Default: `1500ms` - `colour` — The rings' colour. Left out, they take the colour of whatever is being highlighted. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - `easing` — Shape of the rise and fall: `smooth` or `linear`. - Options: `linear`, `smooth` - Default: `smooth` ## Allowed content - Body `{ }`: `line`, `polygon`, `curve`, `circle`, `square`, `rectangle`, `triangle`, `star`, `diamond`, `hexagon`, `vector`, `segment`, `brace`, `angle`, `point`, `text`, `axis.label`, `axis.brace`, `axis.band`, `scatter`, `histogram`, `bar`, `lollipop`, `boxplot`, `density`, `violin`, `fit`, `parameter`, `cue.parameter`, `integral`, `residuals`, `contour`, `heatmap`, `cue.type`, `lines`, `region`, `choropleth`, `marker`, `arc`, `box`, `arrow`, `pin` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `canvas.code` — body `{ }` - `canvas.diagram` — body `{ }` ## Examples ```chalk cue.highlight{ point(x: 4; y: 4; colour: teal) }( at: 2 ) ``` ```chalk cue.highlight{ curve(expression: x^2) }( at: 1 duration: 2s colour: orange ) ``` --- # cue.spotlight Takes the rest of the environment down — the other marks, the grid and the axes — and leaves what it wraps lit. Purely the dimming: nothing is drawn on the content itself, so pair it with a `cue.highlight` where a ring is wanted too. - Kind: Node - Section: Animation & interaction › Motion › Wrapping cues ## Attributes - `at` — When the environment dims: a step index (1), a time (1.5s), or a step with an offset (1 + 500ms). - Implicit - Type: `cue-anchor` - Shorthand: `cue.spotlight:value` - Default: `0` - `duration` — How long the environment stays dimmed, including coming on and letting go. - Type: `duration` - Default: `3s` - `easing` — Shape of the movement: smooth or linear. - Options: `linear`, `smooth` - Default: `smooth` ## Allowed content - Body `{ }`: `line`, `polygon`, `curve`, `circle`, `square`, `rectangle`, `triangle`, `star`, `diamond`, `hexagon`, `vector`, `segment`, `brace`, `angle`, `point`, `text`, `axis.label`, `axis.brace`, `axis.band`, `scatter`, `histogram`, `bar`, `lollipop`, `boxplot`, `density`, `violin`, `fit`, `parameter`, `cue.parameter`, `integral`, `residuals`, `contour`, `heatmap`, `cue.type`, `lines`, `region`, `choropleth`, `marker`, `arc`, `box`, `arrow`, `pin` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `canvas.code` — body `{ }` - `canvas.diagram` — body `{ }` ## Examples ```chalk cue.spotlight{ curve(expression: x^2) }( at: 1 ) ``` ```chalk cue.spotlight{ point(x: 2; y: 4) }( at: 2 duration: 4s ) ``` --- # cue.trace Moves its content along a referenced curve: the anchor point (or the content's centre when no anchor is given) rides the path from the curve's domain start to its end across the window. The motion is x-uniform — constant horizontal speed, true to projectile choreography. - Kind: Node - Section: Animation & interaction › Motion › Wrapping cues ## Attributes - `path` — The line to follow: a curve in this environment by its node id (`path: #flight` for a `curve#flight(…)`), or an expression traced directly (`path: x * (8 - x) / 4`). - Required, Implicit - Type: `curve-path` - Shorthand: `cue.trace:value` - `anchor` — The point of the content that rides the path, e.g. (4, 0). Defaults to the centre of the wrapped content. - Type: `parametised-coordinate-list` - `at` — Anchor: a step index (1), a time (1.5s), or a compound offset (1 + 500ms). - Type: `cue-anchor` - Default: `0` - `duration` — How long the traversal takes. - Type: `duration` - Default: `1500ms` - `easing` — linear or smooth. - Options: `linear`, `smooth` - Default: `smooth` ## Allowed content - Body `{ }`: `line`, `polygon`, `curve`, `circle`, `square`, `rectangle`, `triangle`, `star`, `diamond`, `hexagon`, `vector`, `segment`, `brace`, `angle`, `point`, `text`, `axis.label`, `axis.brace`, `axis.band`, `scatter`, `histogram`, `bar`, `lollipop`, `boxplot`, `density`, `violin`, `fit`, `parameter`, `cue.parameter`, `integral`, `residuals`, `contour`, `heatmap`, `cue.type`, `lines`, `region`, `choropleth`, `marker`, `arc`, `box`, `arrow`, `pin` ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `canvas.code` — body `{ }` ## Examples ```chalk cue.trace{ point(x: 0; y: 0; colour: red) }( path: #flight at: 1 duration: 4s ) ``` ```chalk cue.trace{ text(label: ball) }( path: x * (8 - x) / 4 at: 2 ) ``` --- # cue.type Lines typed in character by character on the timeline — the code environment’s own verb. It wraps `lines` chunks: `in:` says when the typing starts, `speed:` is characters per second (the window’s length is the text’s length over the speed), and `out:` runs the same clock backwards, deleting from the end like a backspace. A caret mark rides the line being typed, and that line carries a wash while the window is open. - Kind: Node - Section: Animation & interaction › Motion › Wrapping cues ## Attributes - `in` — When the typing starts: a step index (1), a time (1.5s), or a step with an offset (1 + 500ms). - Implicit - Type: `optional-cue-anchor` - Shorthand: `cue.type:value` - Default: `none` - `out` — When the deleting starts, in the same form. - Type: `optional-cue-anchor` - Default: `none` - `speed` — Characters per second. - Type: `number` - Also written: `cps` - Default: `25` ## Allowed content - Body `{ }`: `lines` ## Allowed in - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` - `canvas.code` — body `{ }` ## Examples ```chalk cue.type{ lines{ return message }( indent: 1 ) }( in: 1 speed: 30 ) ``` ```chalk cue.type{ lines{ print("draft") } }( in: 1 out: 2 ) ``` --- # cue.to A cue micronode, written inside an animatable attribute’s value. The attribute’s own value is the base state; each `!cue.to` names the value to move to and anchors the move in scene time — `x-domain: [0, 5] !cue.to{[0, 10]}(at: 2)` widens the window at step 2. They chain left to right on one attribute, and a chain may mix `!cue.to` with `!cue.zoom`. - Kind: Micronode - Section: Animation & interaction › Motion › Keyframe cues ## Attributes - `at` — When the move starts: a step index (`at: 2`), an absolute scene time (`at: 1500ms`), or a compound offset (`at: 2 + 500ms`). - Required - Type: `cue-anchor` - `over` — How long the transition takes. - Type: `duration` - Default: `1500ms` - `easing` — The transition’s timing curve. - Options: `linear`, `smooth` - Default: `smooth` ## Allowed content - Body `{ }`: The target value — whatever the attribute itself would accept ## Examples ```chalk zoom: 5 !cue.to{6.5}(at: 2) !cue.to{8}(at: 4 + 1.5s; over: 800ms) ``` ```chalk y: 0 !cue.to{4}(at: 1; easing: linear) ``` --- # cue.zoom A cue micronode for a graph’s `x-domain` / `y-domain` that announces itself: a box is drawn around the smaller of the two windows, held, and only then does the view move — into the box when zooming in, out of it when zooming out. The box never moves in data coordinates. Everything else works exactly like `!cue.to`, and the two can share a chain. - Kind: Micronode - Section: Animation & interaction › Motion › Keyframe cues ## Attributes - `at` — When the zoom begins (the box starts drawing): a step index, an absolute scene time, or a compound offset. - Required - Type: `cue-anchor` - `over` — How long the view takes to move once the box has been drawn and held. - Type: `duration` - Default: `1200ms` - `easing` — The move’s timing curve. - Options: `linear`, `smooth` - Default: `smooth` - `draw` — How long the box takes to draw itself around the region. - Type: `duration` - Default: `700ms` - `hold` — The pause between the box closing and the view starting to move. - Type: `duration` - Also written: `pause` - Default: `250ms` - `colour` — The box is drawn in this palette colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Also written: `color` - Default: `grey` ## Allowed content - Body `{ }`: The target domain, e.g. [2, 4] ## Examples ```chalk x-domain: [0, 10] !cue.zoom{[2, 4]}(at: 2; draw: 400ms; hold: 300ms; colour: blue) ``` --- # Interaction What a reader can move: parameters, a fit they drive, editable environments, and the state that follows them between scenes. ## Parameters A `parameter` is a named quantity with a slider in the environment footer. Its `var` is a single letter, and every expression in the environment that mentions that letter re-evaluates as the reader moves it. ```chalk canvas.graph{ parameter#a(var: a; name: steepness; range: [0, 4]; step: 0.25; default: 2) curve(expression: a * x^2) point(x: 1; y: a; label: $a$) } ``` `name:` is what the parameter is called in front of the reader — rich text, so `Frequency $b$` sets its own maths — and without one the slider is labelled with its letter. `hidden: true` keeps it out of the footer — the expressions still use it, the reader cannot move it — and `lock: true` keeps it out of their reach on an editable environment. Its current position is derived as `#a.value`, readable in prose as `{{#a.value}}`. > A parameter asserts that the narrative holds anywhere in its range, since the reader can reach any value in it. `Parameters and cues` gives the criterion for which changes a document should expose. ## Running a parameter over a column `over:` binds the parameter to a dataset column instead of a written range. The column's cells become its stops, their span becomes its range, and what they are decides how it reads — so a parameter over a column of years is a year scrubber without anything else being said. `range:` is not consulted where `over:` is given: the data decides. ```chalk canvas.graph{ @data#pop{ country | year | millions Brazil | 1960 | 72 Brazil | 1990 | 149 Brazil | 2020 | 213 India | 1960 | 451 India | 1990 | 873 India | 2020 | 1396 } parameter#years(var: t; over: #pop.year; name: Year) bar(x: #pop.country; y: #pop.millions; period: #pop.year) } ``` This is the shape long-format data arrives in — one row per thing per period — and it is the shape a mark's `period:` reads. The mark names the column, not the parameter: they meet at the column. Marks glide between the two rows that bracket wherever the parameter stands, so a reader stopping at 1995 sees half way between the 1990 and 2020 readings; `discrete: true` makes it snap to a row the data actually holds instead. ## Parameters that are not numbers `format:` says how the number reads: `numbers`, `dates`, `months`, `weekdays`, `hours` or `weeks`. Each places a value on its own number line — position 1…12 for a month, a fractional year for a date — so the range stays a pair of numbers and only the readout changes. ```chalk canvas.graph{ parameter#month(var: m; format: months; range: [1, 12]; step: 1; name: Month) parameter#hour(var: h; format: hours; range: [0, 24]; step: 0.25) } ``` `auto`, the default, takes the kind from the column `over:` names, and is numeric where no column was named. Writing `format:` beside `over:` overrules the cells — a column of 1…12 is numbers as far as the data can tell, and only the author knows they are months. ## Letting the scene sweep it `cue.parameter` is the same parameter with the two attributes a cue takes: `at:`, the anchor it starts on, and `duration:`, how long it takes to run its range end to end. Every other attribute is a `parameter`'s. ```chalk canvas.geo:world{ choropleth(regions: #pop.country; values: #pop.millions; period: #pop.year) cue.parameter(var: t; over: #pop.year; at: 2; duration: 12s) } ``` The sweep is choreography like any other: it fires when its anchor does, and the step's own play, pause and replay drive it. `duration:` is the time to cross the whole range, so a `default:` part way along it finishes in proportionally less. The reader still gets the slider, and dragging it displaces the sweep rather than stopping it: playing on carries the letter from wherever they let go, at the rate the author set. A sweep says where to look, not that the reader may not look elsewhere. What moves is the sweep's own elapsed time and never the scene clock — moving that would drag every other cue in the scene along with it, firing choreography the reader had not reached — and their displacement is handed back when they leave the step or replay it. ## Giving fit a parameter for its degree `fit` takes its `degree` as a number or as an expression of parameters. A parameter in that position hands the degree to the reader: the parameter's slider is the control, and the fit recomputes as it moves. ```chalk canvas.graph{ scatter#readings(x: 1, 2, 3, 4, 5; y: 2.0, 2.4, 3.1, 4.8, 7.4) parameter#d(var: d; name: fit degree; range: [1, 4]; step: 1; default: 1) fit#f(of: #readings; model: polynomial; degree: d) } ``` `#f.slope`, `#f.intercept` and `#f.r2` are derived from whatever the reader has chosen, so prose that reads them stays true at every degree. ## editable: environments and lock: `editable: true` on a `canvas.graph` lets the reader add their own elements and pan or zoom the frame; on a `canvas.code` it lets them edit the text, which still runs nothing. Any mark takes `lock: true` to stay out of their reach — and does nothing at all on an environment that is not editable. ## continue: and session state A later environment written as `continue: #projectile` inherits what the reader did in the earlier one: where they left the sliders, the degree they chose, the view they zoomed to. Only that state carries — the content is the new environment's own. --- # parameter A named quantity the reader can vary with a slider. Inside a `canvas.graph` it drives every expression that mentions its letter — a curve of `a * x^2` bends as `a` moves. `name:` is what the slider is called in front of the reader — rich text, so `Frequency $b$` sets its own maths — and without one it is labelled with its letter. The current value is derived: prose cites it with `{{#a.value}}`. It need not be a bare number. `over:` binds it to a dataset column, so its range, its stops and its format come from the cells rather than being written out — a parameter over a column of years is a year scrubber with nothing else said, and every mark in the environment carrying a `period:` over that column slices itself against it. `format:` says how the number reads where the data cannot: `months`, `weekdays`, `dates`, `hours`, `weeks`. To have the scene sweep it rather than the reader, write `cue.parameter`. - Kind: Node - Section: Animation & interaction › Interaction - Aliases: `param` - Writing with it: Parameters & cues ## Attributes - `var` — Variable name (single letter, a-z). - Required, Implicit - Type: `single-char` - Shorthand: `parameter:value` - `name` — What the parameter is called in front of a reader. Rich text, so Frequency $b$ sets its own maths. - Type: `rich-text` - Also written: `label` - `over` — A dataset column the parameter runs over: `over: #pop.year`. Its cells are the stops, their span is the range, and what they are decides the format — so `range:` is not consulted where this is given. This is the shape long-format data arrives in, and the shape a mark's `period:` reads: the mark names the column, not the parameter, and the two meet at the column. - Type: `reference` - Also written: `period`, `column` - `format` — What the values are, for the readout: `numbers`, `dates`, `months`, `weekdays`, `hours` or `weeks`. Each places a value on its own number line — position 1…12 for a month, a fractional year for a date — so the range stays a pair of numbers and only the reading changes. `auto` takes the kind from the column `over:` names, and is numeric where no column was named; writing it beside `over:` overrules the cells, which is what to reach for where a column of 1…12 means months. - Options: `auto`, `numbers`, `dates`, `months`, `weekdays`, `hours`, `weeks` - Also written: `values` - Default: `auto` - `range` — Range interval [start, end]. Ignored where `over:` names a column: the data decides. - Type: `parametised-interval` - Default: `[-10, 10]` - `step` — Distance between stops. Absent, 1 — or, under `over:`, the closest the data's own values come to each other, which is the spacing that can reach every row. - Type: `number` - `default` — Where it starts. Absent, 0 — or, under `over:`, the first value in the column. - Type: `number` - Also written: `start` - `discrete` — Stand on one stop at a time. Continuous (the default), the parameter moves through the gaps between stops and the marks glide with it; `discrete`, it snaps to the nearest — the data's own values, or the `step` ladder — and the marks jump from one slice to the next, for a quantity that is not a continuous function of the axis. - Type: `boolean` - Also written: `stepped` - Default: `false` - `hidden` — Keeps the parameter out of the footer: the expressions still use it, the reader cannot move it. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing it on an `editable:` graph. - Type: `boolean` - Also written: `locked` - Default: `false` ## Derived attributes Computed while the element renders — read one with `#id.name` in attribute position or `{{#id.name}}` in prose, never set. - `value` — Where the letter currently stands — the reader's setting, and the default until they touch it. On a parameter bound `over:` a period column, the period they are standing on. - Derived ## Allowed in - `scene` — detail `[ ]` - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` - `canvas.geo` — body `{ }` ## Examples ```chalk parameter#a(var: a; name: steepness; range: [0, 4]; step: 0.25; default: 2) curve(f: a * x^2; domain: [-3, 3]) ``` ```chalk parameter:t(over: #pop.year; name: Year) ``` ```chalk parameter:m(format: months; range: [1, 12]; step: 1; name: Month) ``` --- # cue.parameter The same parameter, swept by the scene. Identical to `parameter` down to the last attribute, plus the two a cue takes: `at:`, the anchor it starts on, and `duration:`, how long it takes to run its range end to end. A choropleth moving through its years is `cue.parameter:t(over: #pop.year; at: 2)`; the same letter dragged by hand instead is `parameter:t(over: #pop.year)`. It is a cue rather than a `play:` flag because everything that follows from being one is wanted: the anchor is the ordinary one, the host's transport finds the sweep by walking anchors as it finds every other piece of choreography, and the play, pause and replay the reader gets are the step's. `duration:` is the time to cross the whole range, so a `default:` part way along it finishes in proportionally less. The reader still gets the slider, and dragging it displaces the sweep rather than stopping it: playing on carries the letter from wherever they let go, at the rate the author set. What moves is the sweep's own elapsed time and never the scene clock — moving that would drag every other cue in the scene along with it — and their displacement is handed back when they leave the step or replay it. - Kind: Node - Section: Animation & interaction › Interaction ## Attributes - `var` — Variable name (single letter, a-z). - Required, Implicit - Type: `single-char` - Shorthand: `cue.parameter:value` - `name` — What the parameter is called in front of a reader. Rich text, so Frequency $b$ sets its own maths. - Type: `rich-text` - Also written: `label` - `over` — A dataset column the parameter runs over: `over: #pop.year`. Its cells are the stops, their span is the range, and what they are decides the format — so `range:` is not consulted where this is given. This is the shape long-format data arrives in, and the shape a mark's `period:` reads: the mark names the column, not the parameter, and the two meet at the column. - Type: `reference` - Also written: `period`, `column` - `format` — What the values are, for the readout: `numbers`, `dates`, `months`, `weekdays`, `hours` or `weeks`. Each places a value on its own number line — position 1…12 for a month, a fractional year for a date — so the range stays a pair of numbers and only the reading changes. `auto` takes the kind from the column `over:` names, and is numeric where no column was named; writing it beside `over:` overrules the cells, which is what to reach for where a column of 1…12 means months. - Options: `auto`, `numbers`, `dates`, `months`, `weekdays`, `hours`, `weeks` - Also written: `values` - Default: `auto` - `range` — Range interval [start, end]. Ignored where `over:` names a column: the data decides. - Type: `parametised-interval` - Default: `[-10, 10]` - `step` — Distance between stops. Absent, 1 — or, under `over:`, the closest the data's own values come to each other, which is the spacing that can reach every row. - Type: `number` - `default` — Where it starts. Absent, 0 — or, under `over:`, the first value in the column. - Type: `number` - Also written: `start` - `discrete` — Stand on one stop at a time. Continuous (the default), the parameter moves through the gaps between stops and the marks glide with it; `discrete`, it snaps to the nearest — the data's own values, or the `step` ladder — and the marks jump from one slice to the next, for a quantity that is not a continuous function of the axis. - Type: `boolean` - Also written: `stepped` - Default: `false` - `hidden` — Keeps the parameter out of the footer: the expressions still use it, the reader cannot move it. - Animatable - Type: `boolean` - Default: `false` - `lock` — Keeps the reader from editing it on an `editable:` graph. - Type: `boolean` - Also written: `locked` - Default: `false` - `at` — When the sweep starts — a step index (`2`), a time (`1.5s`), or the compound form (`3 + 1.5s`), read exactly as every other cue anchor is. - Type: `cue-anchor` - Default: `0` - `duration` — How long the sweep takes to run the range end to end, written as a duration like every other in the language: `8s`, `1500ms`. - Type: `duration` - Default: `8s` - `easing` — How the sweep is paced. `linear` by default and not `smooth`: a parameter's range is a scale the reader reads values off, and easing in and out of it would make the same span of screen mean different amounts of the quantity at different moments. - Options: `linear`, `smooth` - Default: `linear` ## Derived attributes Computed while the element renders — read one with `#id.name` in attribute position or `{{#id.name}}` in prose, never set. - `value` — Where the letter currently stands: the sweep, displaced by whatever the reader has dragged. - Derived ## Allowed in - `canvas.graph` — body `{ }` - `cue` — body `{ }` - `cue.draw` — body `{ }` - `cue.trace` — body `{ }` - `cue.highlight` — body `{ }` - `cue.spotlight` — body `{ }` - `canvas.geo` — body `{ }` ## Examples ```chalk cue.parameter#years(var: t; over: #rates.year; name: Year; at: 1; duration: 12s) ``` ```chalk cue.parameter:a(range: [0, 4]; at: 2; duration: 3s) ``` --- # data Declares a named dataset — written once and reached from anywhere in the document. It has one of two SHAPES, and the shape decides what can be done with it. TABULAR is columns of strings: inline, the body is pipe-delimited rows under a header row that names the columns; attached, a `.csv` supplies both. GEO is boundary geometry — an attached `.geojson`, which has no columns at all and is bound whole, as a `canvas.geo` basemap. Every tabular cell is a string until an attribute type parses it — the graph's series attributes, for one, read month names, weekday names and ISO dates as axis positions. Reach a column with `{{#name.column}}` to splice its cells as comma-separated text at compile time, or bind `#name.column` where an attribute takes a reference. - Kind: Directive - Section: Directives - Writing with it: Data provenance ## Declaring a dataset `@data` declares a named dataset — written once and reached from anywhere in the document. It has one of two shapes, and the shape decides what can be done with it. **Tabular** is columns of strings. Inline, the body is pipe-delimited rows under a header row that names the columns: ```chalk @data#islands{ island | area | species Baltra | 25.09 | 58 Bartolomé | 1.24 | 31 Santa Cruz | 903.82 | 444 } ``` Attached, a `.csv` supplies both — `@data#survey:galapagos-plants-1973.csv` — with `src` as the implicit attribute and column names taken from the file's header row. Nothing downstream distinguishes the two forms. An id is required in either, because a dataset is reached as `#id.column`. > Datasets are untyped: every cell is a string until an attribute's declared type parses it, exactly as any literal. A blank cell is an empty string — Chalk has no missing value. ## Boundaries as a dataset The second shape is **geo**: boundary geometry, attached as a `.geojson`. It is what a `canvas.geo` environment draws when the boundaries it needs — electorates, catchments, council wards — are not one of the committed packs. The extension *is* the format declaration: a `.csv` is tabular, a `.geojson` is geo, and `.json` is not admitted, so the compiler never guesses at a file's shape or reads it to find out. ```chalk @data#wards:sydney-wards.geojson @data#turnout:turnout.csv canvas.geo{ choropleth(in: #wards; regions: #turnout.ward; values: #turnout.pct) }( projection: conic ) ``` A geo dataset is **opaque**. It has no columns, so it is bound whole — `in: #wards` — and `#wards.name` is a compile error that names the fix rather than a lookup that quietly finds nothing. Which feature property names each region is said on the environment consuming it, with `names:`, because that is a question about the join and not about the file. Only a tabular dataset can be written inline: a `{ }` body is always columns. ## Reaching columns A tabular dataset's columns are reached by ordinary member access, **fully qualified**. There is no bare-column shorthand: attribute values are unquoted, so `label: island` must remain the literal string `island`. Two mechanisms consume a column, distinguished by the braces: - `{{#survey.area}}` — an **expression**: the cells, joined with commas, resolved while the document compiles. Works in prose, code bodies and attribute values alike. - `#islands` / `#islands.column` — a **binding**: a reference carried through to the renderer, for attributes typed to accept one. ### Substitution against binding A column reading resolves at compile time because a dataset is immutable by construction — nothing replaces it and no cell depends on a varying value, so there is nothing for the expression to wait on. A value that does vary at render time — a derived attribute, an animated attribute, a reader-set parameter — makes the expression **bound** instead of refusing it: `{{#fit.slope}}` compiles, and ships as a tree the renderer evaluates every frame rather than as compile-time text. ## Tables from data A `table` binds a dataset directly and projects columns out of it: ```chalk table(data: #islands; columns: island, area, species) ``` `columns:` is a list of plain strings the table resolves against its own `data:` — library-interpreted names rather than references, which is why they are written bare. Every other consumer receives a column as text through substitution: a `code environment` splices `{{#survey.area}}` into a numpy array literal. ## Columns as values Beyond the table, a column is bound where an attribute is typed to take one, and every such attribute takes **one column**. The consumers: - The graph's data marks — `scatter`, `line`, `polygon`, `bar` — take a *series* per axis, `x:` and `y:`, and a `period:`. - `choropleth` takes `regions:`, `values:`, `intensity:` and `period:`; `marker` takes `lat:`, `lon:` and `size-by:`. - `parameter` and `cue.parameter` take the `over:` column they run across, in any environment. - Everything else — prose, a `code environment`, an attribute typed for a list — receives a column as text by substitution, `{{#survey.area}}`. ### One column per attribute A column binds one attribute, never a pair: `scatter(x: #islands.area; y: #islands.species)`, `marker(lat: #cities.lat; lon: #cities.lon)`. Parallel columns stay **row-aligned** — a blank cell is kept in place as no value rather than dropped — and where a consumer pairs two columns it drops the *pair* whose either half it cannot read, so a gap in one column never shifts the readings of the other. The graph marks alone also take a series *written out* beside a column: ```chalk scatter(x: 1, 2, 3; y: 2.0, 2.4, 3.1) scatter(x: #islands.area; y: #islands.species) bar(x: North, South, East; y: 42, 31, 55) choropleth(regions: #gdp.country; values: #gdp.gdppc) ``` ### The two readings of a series entry A written series entry is read two ways, because the axis it lands on is what decides. A number is a number. `2 * k` is an expression of the parameter `k` — and so, to the expression parser, is `Red`, which reads as `R·e·d`. So an entry keeps both its expression and the text it was written as, and the axis picks: on a numeric axis the expression is evaluated, on a categorical one the text is looked up among the axis's names. An entry that is neither — `Strongly agree`, which no expression grammar accepts — is a name and nothing else. Which way an undeclared (`auto`) axis goes follows from that. An entry counts as a name when it is not a number and not an expression whose every letter is a declared `parameter`; a series with a name on it rules the axis in names, in the order they first appear. So `x: a, b` beside `parameter(var: a)` and `parameter(var: b)` is two numbers, and beside nothing is two categories. The whole rule is on the `graph guide`; the consequence for data is that a column of names needs no declaration to be read as names, and a name the axis does not have — under `x format: North, South`, a row reading `West` — is a compile error naming the row. ### What a column reads as A column's kind is decided for the column, not cell by cell: reading `2024-03-15` one cell at a time gives `2024`, and a year of daily readings lands on one position. A column is **numeric** when every non-blank cell is a number; a **date** when every one is an ISO date, `2024-03-15` or `2024-03`, read as a fractional year so that a domain and a fit's slope still speak plain numbers; and **text** otherwise — names. Each consumer reads the kind its own way. A graph axis takes numbers and dates as positions and labels a date column in whole years under `auto`; text rules the axis in names, and a text column on an axis declared `linear` is an error rather than a column of dropped rows. A choropleth shades a numeric `values:` column through its ramp and enumerates a text one into categories, each with a flat hue. A marker's `lat:` and `lon:` are numeric or nothing. Month and weekday names are text like any other, matched by full name or first three letters against the `month` and `weekday` presets, and never turned into numbers on the way in. ### Rows over time Data with a state per period — per year, per month, per hour — is written **long-format**: one row per thing per period, with a column naming the period. A consumer names that column as its `period:` — a graph mark, a choropleth — and a `parameter` in the same environment names it as its `over:`. They meet at the column, not at each other. The consumer then shows the slice at whatever the parameter holds, gliding between the two periods that bracket it, so a reader stopped between 2023 and 2024 sees half way; a thing whose own rows start late or end early holds its nearest reading rather than vanishing. Where the quantity is not a continuous function of time — a count of days, a result per election — `discrete` on the parameter makes it stand on one period at a time, and the consumer jumps from slice to slice. A graph's frame is built from every period at once and holds still as they scrub. ```chalk @data#calendar{ year | month | days 2023 | January | 31 2023 | February | 28 2024 | January | 31 2024 | February | 29 } canvas.graph{ bar(x: #calendar.month; y: #calendar.days; period: #calendar.year) parameter#years(var: t; over: #calendar.year; name: year) }( x format: months ) ``` The period column is read by kind, and the scrubber reads it back in the same form: years and plain numbers as themselves; ISO dates as fractional years, shown as the nearest day; month and weekday names as their positions, shown by name; `HH:MM` as hours; `YYYY-Www` as weeks — the same vocabulary a parameter's `format:` writes out, which is what to reach for where the cells cannot say (a column of 1…12 that means months). The parameter owns a letter like any other, and `{{#years.value}}` cites the period in prose. Where a mark's `period:` column is not one any parameter names, it slices against the environment's sole bound parameter. Writing `cue.parameter` instead hands the scrubbing to the scene. ## One mark per row Binding a column hands a whole column to one attribute, which is what a `scatter` or a `line` wants. The other shape is one mark per *row* — a parallel-coordinate plot, a box per subject, a card per observation — and that is `@for` over the dataset itself: `@for:#islands` walks its rows top to bottom. ```chalk @data#islands{ island | area | species Baltra | 25.09 | 58 Bartolomé | 1.24 | 31 Santa Cruz | 903.82 | 444 } @for:#islands{ {{i}}. {{i.island}} covers {{i.area}} km$^2$ and carries {{i.species}} species. } ``` The loop binding carries both halves of the row. `{{i}}` is the row number from 1 — the same value `@for:3` binds, so it works in prose, in arithmetic (`{{i + 1}}`) and in an id (`#isle-{{i}}`) — and `{{i.}}` is that row's cell, spliced as text and parsed by whatever position it lands in, exactly as if it had been typed there. `as:` renames both: `@for(n: #islands; as: row)` gives `{{row}}` and `{{row.area}}`. A cell is an operand as well as text, so `{{row.area * 2}}` is arithmetic on it. A column name with a space follows the usual key spelling — `{{i.log area}}` or `{{i.log-area}}`, and the unhyphenated form in arithmetic. A pack's dataset works through its prefix: `@for:#statistics.faithful`. ```chalk canvas.graph{ @for(n: #soil; as: row){ line#profile-{{row}}(x: 1, 2, 3; y: {{row.sand}}, {{row.silt}}, {{row.clay}}) } } ``` Rows are the only thing a loop goes through: to work down one column, loop over the dataset and read that column. There is no filter and no sort — an `@if` inside the body is the filter. The dataset has to be declared **above** the loop, since `n` is read as the document compiles; naming a column there (`@for(n: #islands.area)`) is refused and points at `{{i.area}}`; so are a geo dataset, which has no rows, and an attached file this build cannot read — refused rather than run zero times, which would let one document mean two things. Cells are substituted at compile time, and a dataset cannot change, so nothing a loop writes can go stale. ## Scope and limits - Directives have no placement rules — a `@data` is document-visible wherever it is written, including inside an `@include`d partial. A `@for` over one is the exception: it reads the rows as it compiles, so the `@data` must come first. - Attached data is `.csv` or `.geojson`, and nothing else — not `.json`, not a URL. - No inline geo data. Boundaries arrive as a file. - No computed columns, row filters or aggregates on a tabular dataset. A transform belongs upstream, stored in the data, which keeps column access a pure lookup and columns substitutable; the one thing that iterates is `@for`, and its filter is an `@if` in the body. - `@data` holds tables of values; `@let` holds scalars and repeated fragments. ## Attributes - `src` — The file the dataset is filled `from:` a `.csv`, whose header row names the columns, or a `.geojson`, which becomes boundaries for a `canvas.geo` environment. The extension IS the format declaration, so `.json` is not admitted and nothing else is guessed at. Omit it to declare tabular rows inline in the body instead. Written as shorthand: `@data#survey:plants.csv`. - Implicit - Type: `string` - Shorthand: `@data:value` ## Allowed content - Body `{ }`: header and rows, pipe-delimited (inline form only) ## Examples ```chalk @data#islands{ island | area | species Baltra | 25.09 | 58 Pinta | 60.0 | 94 } ``` ```chalk @data#survey:galapagos-plants-1973.csv ``` ```chalk canvas.code{ lines{ area = np.array([{{#survey.area}}]) } }( language: python ) ``` ```chalk @data#wards:sydney-wards.geojson @data#turnout:turnout.csv canvas.geo{ choropleth(in: #wards; regions: #turnout.ward; values: #turnout.pct) }( projection: conic ) ``` ## Notes - An attached `.geojson` is OPAQUE: it has no columns, so `#wards.name` and `{{#wards.name}}` are both compile errors that name the fix — bind the dataset itself, `in: #wards`. Which feature property names each region is said on the consuming environment with `names:`, not here. - Only a tabular dataset can be written inline. A `{ }` body is always columns, so there is no inline form of geo data. --- # define Declares a template — a node shape written once and stamped out wherever its name is used. The implicit attribute `node` names the type being declared (`@define:card`); the attribute group declares the template’s own attributes, each with a TYPE written `name: !(…)`, and `{{name}}` splices the value into the body. `body` and `detail` are reserved names that declare the template’s SECTIONS rather than attributes: `{{body}}` and `{{detail}}` splice the content of the use’s own `{...}` and `[...]`. Because the declarations make a real node type, a use is checked where it is written — a value the type cannot parse is an error at the call site, naming the template. A template may use other templates, but circular expansion is a compile error. An id written literally in the template is private to each use, so two uses never collide; an id spelt through an attribute (`figure#{{fid}}`) is the caller’s, document-wide. - Kind: Directive - Section: Directives ## Attributes - `node` — The name of the type being declared, written as shorthand: `@define`:card. It cannot also be a template parameter. - Required, Implicit - Type: `string` - Shorthand: `@define:value` ## Allowed content - Body `{ }`: The template — the Chalk that replaces each use, with {{param}} splices and {{body}}/{{detail}} for the use’s own sections ## Examples ```chalk @define:swatch{ square(x: 0; y: 0; size: 1; colour: {{colour}}) }( colour: !colour(default: blue) ) swatch(colour: red) ``` ```chalk @define:finding{ note{ {{body}} } } finding{Richness scales as roughly the cube root of area.} ``` ```chalk @define:reading{ point(x: {{x}}; y: {{y}}; label: {{name}}) }( x: !number(required; implicit) y: !number(required) name: !string(default: —) ) reading:2(y: 4.1; name: Baltra) ``` ## Notes - Each attribute is declared `name: !(…)`, parentheses included even when empty (`!string()`) — a bare `!name` is a value fold missing its body. Inside the group, `required`, `default`, `aliases` and `implicit` describe the ATTRIBUTE; everything else refines the type (`!number(min: 0)`, `!reference(targets: @data)`). Join alternatives with `|`. - An attribute that is neither `required` nor given a `default` binds nothing when a use omits it, so a template that splices it gets the ordinary undefined-constant error. Give every attribute a default unless the use must always supply it. - `body:` and `detail:` declare the template’s sections, using the same policy forms an element has: `!all()`, `!none()`, `!literal()`, `!children(scene, figure)`, `!inline(content: all)`. They default to `body: !all()` and `detail: !none()`, so a template that splices `{{detail}}` must declare `detail: !all()` or every use is refused its `[...]` section. - A literal id in the template compiles to `id~1`, `id~2`, … numbered by use in document order. A reference written in the template resolves to the same use’s copy first, then outward — the enclosing template’s, then the document’s; a name declared both in the template and outside it is an ambiguity error, never a shadow. - Block content passed through a slot keeps the caller’s ids: `card{ figure#mine{…} }` leaves `mine` document-wide. - `@data` declared inside a template is private per use like any id, and canvas attributes (`x: #pts.x`) cannot reach a private dataset — declare the `@data` at document level, or name it through an attribute (`@data#{{name}}`). - `preset:` does not apply to a use, and a use is not checked against its parent’s content policy — the nodes it expands to are, where they land. --- # for Repeats its body. The implicit attribute `n` is a non-negative count (`@for:3`), an inclusive range (`@for:0..6`) that iterates upward, or a tabular dataset (`@for:#islands`), which iterates its rows top to bottom. Inside the body `{{i}}` splices the counter — the 1-based row number over a dataset — and over a dataset `{{i.}}` splices that row's cell; `as:` renames both halves. Ranges must ascend — a descending `from..to` is a compile error. - Kind: Directive - Section: Directives ## Attributes - `n` — How many times, or over what: a non-negative integer count (`@for:3`), an inclusive `from..to` range (`@for:0..6`) that iterates upward, or a tabular dataset by reference (`@for:#islands`, `@for:#statistics.faithful` for a pack's), whose rows it walks in order. The dataset must be declared ABOVE the loop, and naming a column (`#islands.area`) is a compile error — loop over the dataset and write `{{i.area}}`. A geo dataset has no rows, and an attached file this build cannot read is refused rather than run zero times. - Required, Implicit - Type: `number | interval | reference` - Shorthand: `@for:value` - `as` — Names the binding — `as: k` makes the splice `{{k}}` instead of `{{i}}` — which is how nested loops tell theirs apart. Over a dataset it renames both halves: `{{row}}` and `{{row.area}}`. Must be a valid identifier. - Type: `string` ## Allowed content - Body `{ }`: The content to repeat — spliced once per iteration with {{i}} (and, over a dataset, {{i.column}}) substituted ## Examples ```chalk @for:0..4{ point(x: {{i}}; y: {{i}}) } ``` ```chalk @for(n: 1..3; as: k){ Attempt {{k}} of 3. } ``` ```chalk @data#islands{ island | area Baltra | 25.09 Bartolome | 1.24 } @for:#islands{ {{i}}. {{i.island}} covers {{i.area}} km$^2$. } ``` ```chalk @for(n: #soil; as: row){ line#trace-{{row}}(x: 1, 2, 3; y: {{row.a}}, {{row.b}}, {{row.c}}) } ``` --- # if Compiles its body only when every condition holds. The attribute group IS the conditions — each `key: value` tests a `@let` constant by exact value — so `@if` has no attributes of its own. At least one condition is required, and a condition naming an undeclared constant is a compile error. - Kind: Directive - Section: Directives ## Allowed content - Body `{ }`: The content to keep when the conditions hold — any Chalk that is legal where the @if is written ## Examples ```chalk @let(audience: teacher) @if(audience: teacher){ note{ Answers are collected in the closing scene. } } ``` --- # ignore A block comment. The body is discarded before parsing — for drafts, notes to self, or content taken out of circulation without deleting it. Braces inside still need to balance; it takes no attributes and no implicit value. - Kind: Directive - Section: Directives - Writing with it: Source style ## Allowed content - Body `{ }`: Anything — the body is discarded unparsed ## Examples ```chalk @ignore{ scene{ # A draft scene, kept but not compiled } } ``` --- # include Splices a partial — a .part`.chalk` attachment — into the document at compile time, exactly where the directive is written. A dataset or constant declared in an included file is visible to the whole document. - Kind: Directive - Section: Directives ## Attributes - `src` — The partial's file name. Written as shorthand (`@include`:overview.part`.chalk`) or explicitly (`@include`(`src:` overview.part`.chalk`)). - Required, Implicit - Type: `string` - Shorthand: `@include:value` ## Examples ```chalk @include:overview.part.chalk ``` --- # let Declares named constants. Each binding is read back by writing `{{name}}` — a compile-time splice of the value as text, which works anywhere the source is read: prose, attribute values, code bodies. The attribute group IS the bindings, so `@let` has no attributes of its own and no shorthand form (`@let`:x is an error). For tables of values, declare a `@data` dataset instead. - Kind: Directive - Section: Directives - Writing with it: Source style ## Examples ```chalk @let(source-note: Johnson & Raven (1973)) As reported in {{source-note}}. ``` ```chalk @let( sample-size: 240 site: Baltra ) We surveyed {{sample-size}} plants on {{site}}. ``` ## Notes - A splice is textual and happens before parsing, so `{{…}}` is not an element and cannot be styled or nested. A value that changes at render time cannot be spliced — read it with a binding instead. - `body` and `detail` are reserved across the constant namespace — inside an `@define` template they name the use's own `{...}` and `[...]` sections. Binding either as a constant is a compile error (D6). --- # preset A reusable attribute bundle. `@preset:text(size: 24)` is anonymous and applies to every `text` node; `@preset#accent(...)` is named and applied by hand with `preset: #accent`; `@preset#accent:text(...)` is both named and scoped to one type. The attribute group holds the target type’s attributes — which is why `node` itself cannot be set by a preset. Presets apply to block nodes only, must be declared before use, cannot reference other presets, and share one identifier namespace with every other `#id`. - Kind: Directive - Section: Directives ## Attributes - `node` — The node type the preset scopes to, written as shorthand: `@preset`:text. Omit it for an unscoped preset applied by id. - Implicit - Type: `string` - Shorthand: `@preset:value` ## Examples ```chalk @preset:text(size: 24) ``` ```chalk @preset#accent(colour: orange; text-size: 24) curve(expression: x^2; preset: #accent) ``` --- # use Imports a pack — a named collection of ready-made constructs, datasets and presets — and makes its public names available under that name. `statistics.normal(…)` is a construct from the `statistics` pack; the prefix is always written, so where a construct came from is visible at every use. A pack’s datasets are reached the same way (`#statistics.anscombe.y1`). Only what a pack publishes can be reached: the rest is its own workings. A pack cannot see anything in your document — its constants, its ids, its own templates are invisible to it, and yours to you. - Kind: Directive - Section: Directives ## Attributes - `pack` — The pack’s name, written as shorthand: `@use`:statistics. - Required, Implicit - Type: `string` - Shorthand: `@use:value` - `as` — A different prefix to call the pack’s constructs under, for when two packs share a name. It REPLACES the pack’s own name — under `as: st`, `statistics.normal` no longer resolves. - Type: `string` ## Examples ```chalk @use:statistics canvas.graph{ statistics.normal(mu: 0; sigma: 1) }( x domain: [-4, 4] y domain: [0, 0.45] ) ``` ```chalk @use(pack: statistics; as: st) canvas.graph{ st.uniform(a: 0; b: 1) } ``` ## Notes - The prefix is not optional: there is no unqualified import. Two packs can therefore never collide, and a reader can always tell a pack construct from a built-in one. - What a pack draws is ordinary chalk — the compiled document is the same as if you had written the nodes out by hand. - The same pack may be imported twice only under different prefixes. --- # What a document covers One topic, the constructs that topic needs, and the length covering it takes. ## A whole document, end to end A file is a `header`, whatever constants and datasets the document declares, and a sequence of `scenes`. Each scene is a narrative in `{ }` and a canvas in `[ ]`, and the step markers in the narrative are what the canvas advances against. Everything else in this manual is the vocabulary those two slots take. ```chalk *( title: Why Euclid's algorithm terminates description: The remainders strictly decrease, and no such sequence is infinite. tags: number-theory, algorithms lesson ) scene{ Two numbers go in and the same rule runs on them over and over: $(a, b) \leftarrow (b, a \bmod b)$. Nothing about that says it ever stops. === It does, and the reason is the second coordinate. A remainder mod $b$ is smaller than $b$, so every step hands the next one a strictly smaller number to work with. }[ canvas.graph{ // The pair (1071, 462) traced to (21, 0). line(x: 0, 1, 2, 3; y: 462, 147, 21, 0; colour: teal) cue{ axis.label(y: 0; label: the remainder reaches zero; line) }( in: 1 ) }( x axis label: step y axis label: second coordinate ) ] scene{ A strictly decreasing sequence of whole numbers bounded below by zero is finite, so the algorithm runs out of steps rather than out of patience. definition{ A rule stating that a strictly decreasing sequence of non-negative integers is finite — the argument every termination proof of this shape rests on. }( term: Well-ordering of the naturals ) } ``` ## The topic as the deliverable A document teaches one topic, and the header's `title` names it. Every scene answers to that name: a reader who has read the title can predict what the document holds, and a scene they could not have predicted needs a reason the prose gives them. ## Constructs the topic did not ask for The commonest departure is a canvas environment nobody asked for — a Python environment in a document about a statistical result, a plot of something adjacent in a document about a historical one. The test is whether the construct carries the topic or decorates it. A topic that *is* code needs code; a topic about a relationship between two quantities needs that relationship plotted; a topic that is neither needs neither, and what fails the test is a second document rather than a scene of this one. ## Length as an outcome A document covers its topic and stops, and its scene count is what that took. A scene written to reach a length is padding, and it compiles exactly as well as the rest of the document. ## Breadth of vocabulary as a non-goal No construct is reached for because the document has not used it yet. A document that uses four constructs because its content needed four is better than one that uses nine: the ninth is a form the reader has to read for the first time, spent on nothing the argument required. `Selecting canvas content` gives the same test for a single mark. The reverse is not a defect. Three tables in a row is three scenes doing the same kind of work, and the fix is in the scenes rather than in the canvas — repainting the third as a graph leaves the repetition where it was and adds a form the content did not ask for. ## internote, note and lesson These documents are **internotes**, in prose and in interface copy alike. In this vocabulary a bare *note* is the `note` callout and nothing else. `lesson` in the `document header` flags structured teaching material, which is what Explore's Lessons filter reads — a claim about what kind of document this is, not about how good it is. --- # Prose & canvas state Prose analyses the state the canvas is in. It never captions the interface. ## Captioning the interface instead of the state Prose that describes controls rather than states — *drag the slider to see what happens*, *notice how the curve changes* — instructs the reader to operate the interface and leaves the conclusion undrawn. The constraint is the opposite: prose describes the state the canvas is in, not the controls that reach it. A sentence that remains true when the canvas is replaced by a screenshot of a slider is a caption rather than an argument. ## Steps as anchors for claims A `step` advances the scene and cues fire against it, so the sentence at step two stands beside the canvas as it is at step two. One anchor drives both, and along the authored path the prose and the canvas agree by construction. ```chalk scene{ Fitted straight, the residuals fan out at both ends — the model is missing curvature, not noise. === A quadratic term flattens them. }[ canvas.graph{ scatter#readings(x: 1, 2, 3, 4, 5; y: 2.0, 2.4, 3.1, 4.8, 7.4) cue{ fit#linear(of: #readings; model: polynomial) }(in: 0) cue{ residuals(of: #linear) }(in: 0) } ] ``` ## Every marker anchored, every anchor reachable A step marker is a timeline trigger, not a paragraph break — paragraphs separate with a blank line. Three things follow, and all three are checked against the whole set of markers rather than against the largest one. - A scene whose detail never changes — a table, an equation, a code environment with no cues — carries **no** markers. - Every marker has something anchored to it. Cues at `in: 0` and `in: 2` under two markers leave step 1 firing nothing, so the reader advances the scene and watches it stand still. Neither is a compile error: a scene with three markers and one cue is a valid scene. The third rule is, and it is the mirror of them — an anchor past the last marker is refused, because the step it names does not exist. ```chalk scene{ One. === Two. }[ canvas.graph{ cue{ point(x: 1; y: 1) }(in: 3) } ] ``` Rejected by the compiler: `[semantic]` 'cue.in' anchors to step 3, but the scene has 1 step (valid: 0–1) > The same count governs a keyframe: `!cue.to` and `!cue.zoom` take `at:` on the same clock, and are refused the same way. `Motion` covers the anchors themselves. ## Reader exploration off the authored path A reader moving a parameter leaves the authored path. The departure is reversible: scrubbing back re-synchronises the canvas with the narrative. The constraint is not that unnarrated states are unreachable, but that every state a document exposes is one its prose remains true in. `Parameters and cues` covers which states a document should expose. ## Five checks on a sentence - A claim names its subject. *The graph shows a relationship* names none. - No sentence takes a control as its subject; `slider`, `drag`, `click` and `hover` in narrative prose indicate the failure. - A number in prose is read live rather than typed — see `Reading computed values in prose`. - One claim per step. A sentence requiring an *and* is usually two. - The sentence is about the canvas at *this* step, not the one two steps on. Prose describing a mark the reader cannot see yet is a caption written early. --- # Prose & headings What the narrative carries that the picture cannot, and headings a reader can navigate by. ## Explaining rather than narrating The canvas is beside the prose and the reader can see it. A sentence restating what is drawn — *the line rises steeply, then flattens* — spends a step on what is already on the screen, and the reader reaches the next step having been told nothing. The narrative carries what the picture cannot: why the state is what it is, what it costs, and which part of the argument it settles. ## Referring to the canvas Where the detail shows something, the prose names it — the mark, the value, the region — so the reader knows which part of the picture the sentence is about. A mark is named by its `label:` rather than by where it sits: *the upper line* stops being true the moment a domain changes, and means nothing at all in a scene where the reader can pan. A narrative that never refers to its detail, and a detail nothing in the narrative points at, are two documents side by side. ## Scaffolding, and adjectives in place of explanation A scene does not announce what it is about to cover or summarise what it covered. Rhetorical setup — *for a stronger reason than you might expect* — and restating tails come out in the same pass, and what is left is the argument. Adjectives are not explanation. *The elegance here is remarkable — a single idea that unlocks everything that follows* gives the reader nothing to do; *the same subtraction then runs on the remainder, so every step shrinks the pair* gives them the mechanism. Where a step matters, what it does is what to write. ## Headings that name their content A heading names its content. A reader scanning a document is looking for a subject, and a heading that gestures at one costs them the scan: *Where the method wastes its time* cannot be searched for, where *The repeated subtractions at the tail* can. Narrative headings are the one place teaching as a story is paid for by somebody else. ## The opening scene and the four levels The first scene opens on prose. The title is already at the top of the page, and a heading immediately beneath it says the same thing twice — an `h1` that does open the first scene is set as the document's opening heading, which is worth having only where the document's first part has a name of its own. Below that, all four levels are available and a document of any length uses more than one: `#` for the document's parts, `##` for the sections inside them, `###` where a scene turns a corner. Heading 1 is not reserved for the title — the title comes from the header — so it is free for the parts. ## Dangling pronouns after a promoted sentence Promoting a sentence into a heading leaves the paragraph beneath it pointing at something that is no longer in it. *This*, *that chain*, *each line* are the words to re-read: they resolved against the sentence that moved. It is the defect editing introduces most often, and it survives every mechanical check there is. ## Maths as LaTeX Every mathematical expression is set as LaTeX wherever LaTeX is legal: `$…$` inline, `equation` on a line of its own, and inside every `rich-text` attribute — a mark's `label:`, a table cell, a heading, a `definition`'s `term:`, an environment's `title:`. An ASCII stand-in is not a smaller version of the same notation; it is different notation, and the reader has to decode it. ```chalk $(a, b) \leftarrow (b, a \bmod b)$ not (a, b) <- (b, a mod b) $18 \times 18$ not 18 x 18 ``` --- # Callouts One job each, and a definition written to survive in a glossary beside definitions from other documents. ## Prose before a callout The four callouts are narrative content: `definition`, `note`, `example` and `task` are legal in a scene's body and in no canvas. Each has one job, and each is introduced by the prose it interrupts: at least a sentence stands before the first callout in a scene, and two never stack with nothing between them. A run of callouts is a scene whose argument has been replaced by its asides. ## definition and the glossary `term:` is the implicit attribute — the word being defined — and the body is the definition. Terms are collected into glossaries across many documents, so a definition is read by people who never saw the scene it came from, beside definitions written by other authors. Two rules follow from that, and both are about the first few words. ### A term that survives on its own The term names its own subject. `Divisibility` is a term; `Measures` is a word the scene happened to be using, and in a list it is either meaningless or somebody else's. A term is rich text, so notation is part of it where the notation is the name — `$\epsilon$-$\delta$ continuity`. ### Naming the kind of thing first The body opens by naming what kind of thing the term is — a method, a theorem, a probability, a relation, a rule, a quantity, a data structure, a task. Someone meeting the word cold needs its category before its content. It is named as the grammatical head of a sentence rather than as a label before a colon, and the sentence continues as prose: - *Linking by a fixed rule over the comparison vector.* — a gerund, so the reader is never told what kind of thing this is. - *A linkage method: a fixed rule over the comparison vector.* — a tag welded to a fragment. - *A linkage method that applies a fixed rule to the comparison vector, requiring a set combination of agreements on every pair.* An opening formula fails the same test from the other side. *$ax + by = \gcd(a, b)$ for some integers $x$ and $y$* is the statement itself; *a theorem stating that for any integers $a$ and $b$ there are integers $x$ and $y$ with $ax + by = \gcd(a, b)$* is a definition of it. ```chalk definition{ A quantity that stays the same under a given transformation. }( term: Invariant ) ``` ### What earns a definition One threshold, held across the document. *Every term the document relies on later, defined where it is first used* is a workable one. The document's own flagship concept is defined under it too — it is the term most likely to be looked up, and being obvious to the author is not a reason to leave it out of the glossary. Bodies stay glossary-length: a sentence or two, the equation where the concept is mathematical, and no editorial tail. ## note as an aside A note holds something set aside from the line of argument: a piece of history, a related result, a warning about a case the document does not cover. The test is whether a reader who skips it loses the thread. Content that is the next step of the argument is a paragraph, and putting it in a note tells the reader they may skip the argument. ## example as a worked case An example works an instance: values in, the steps, the result. An example that re-explains the idea in other words is a paragraph that has been boxed, and the box is making a promise about specificity the content does not keep. ## task and its held-back half A task poses an exercise. Its optional detail is the half held back — a hint or the solution — rendered inside the callout and collapsed until the reader asks for it, so a task with an answer keeps the answer out of sight rather than out of the document. ```chalk task{ Show that the residuals of the quadratic fit sum to zero. }[ Every least-squares fit with an intercept term does: the normal equation for the intercept IS that sum set to zero. ]( title: Why the intercept forces it ) ``` --- # Selecting canvas content A mark belongs on the canvas only if seeing it changes what the reader understands. ## A mark's effect on the reader's understanding A mark belongs on the canvas when its presence changes what the reader understands. Relevance and accuracy are not sufficient: a mark whose removal leaves the argument equally clear is decoration, and competes for attention with the marks carrying the argument. ## The detail read on its own A detail can be opened full screen, without the narrative beside it. What is on the canvas therefore has to hold as a picture: the marks named by their own `label:`, the axes saying what they hold, the environment's `title:` naming its subject where it takes one. The body is what a lecturer says and the detail is what is on the board — and a board that reads only while someone is talking over it is a board with the labels missing. ## Four failure modes - **Restating the prose.** A `text` mark repeating the adjacent sentence adds nothing to it. - **Decorative accuracy.** Gridlines, ticks and axis labels the argument never refers to. `show-grid`, `show-ticks` and `show-axes` control each independently. - **Simultaneous reveal.** Four curves drawn at once where the argument concerns one. `cue` reveals each as the prose reaches it. - **Unlabelled marks named by position.** A mark the prose calls *the upper line* carries a `label:` instead. ## Two environments maximum, one claim each A scene detail holds at most two canvas environments, and `direction` and `ratio`arrange them — data beside the code producing it, a function beside its residuals. A third environment is a compile error rather than a layout decision: Leave `direction` at its default `auto`. A split canvas is read twice — in the column beside the prose and again full-screen in presentation — and the axis that works in one is the wrong one in the other, so `auto` stacks the pair while reading and sets them side by side while presenting. Naming `horizontal` or `vertical` outright is for a split whose axis is part of what it says, not for the arrangement that looked right in whichever of the two you happened to be in when you wrote it. ```chalk scene{ Three things at once. }[ canvas.graph{ curve(expression: x^2) } canvas.graph{ curve(expression: x^3) } canvas.code{ lines{ y = x ** 2 } }( language: python ) ] ``` Rejected by the compiler: `[semantic]` A scene detail holds at most two canvas environments; found 3 > A later scene returning to the same subject continues the earlier environment by id — `continue: #projectile` — so the reader retains the state they reached rather than meeting a reset canvas. ## The empty detail A scene detail is optional. A scene stating a definition, posing a question or drawing a conclusion may carry none; a canvas left standing from the preceding argument describes that argument rather than the current one. --- # Framing a readable graph The frame, the defaults, the room a label needs, and the one meaning a hue carries through a document. ## Authored and derived domains An omitted domain is derived from the marks and then *corrected*: the tighter axis grows about its centre until one unit of x covers the same distance on screen as one unit of y. An authored domain is used exactly as written, aspect and all. The correction only ever grows an axis the graph derived, and it does not run at all where the units are not comparable — a categorical axis, a `log` axis, the number line. ### Shapes whose proportions carry meaning A circle drawn on a frame whose axes are scaled differently is an oval, and the reader has no way to tell it from an oval the author meant. Where a shape carries meaning — a circle, a square, a right angle, any construction a reader is asked to see the geometry of — **both** domains are omitted, the correction runs, and the shape survives. ### Layouts that fill the frame Where nothing turns on shape — annotated rows, labelled bands, a sequence laid out along an axis — **both** domains are authored, and the frame is exactly the window written. Authoring one and omitting the other is the case to avoid: the omitted axis is grown against the authored one, so the frame is neither the window asked for nor a corrected one. ### Padding a domain past the data A domain is not widened to buy a margin. `[-4, 54]` for readings spanning 0 to 48 puts values the data never took into the axis gutter and crowds what is left. The honest bounds, or none at all. Room for a label at the end of a series is a different question, answered under *Labels, clipping and the frame edge* below. ## size and width as house style The `size` and `width` defaults are the house style, and a mark that sets them stops matching every mark in the document that does not. The exception is where the attribute is the geometry rather than the weight: a `square`'s `size` is half its side length and a `circle`'s is its radius, so removing those removes the shape's dimensions. ## One declaration per mark Each mark is declared once. To ring or dim around something a cue has already brought in, the `cue.highlight` or `cue.spotlight` nests *inside* that cue: neither changes whether its content is there, so the mark keeps the presence the outer cue gave it. ```chalk cue.draw{ circle(x: 2; y: 3; colour: teal; label: P) cue.highlight{ circle(x: 6; y: 1; colour: teal; label: Q) }( at: 2 duration: 2s ) }( in: 1 in duration: 1.2s ) ``` Two identical shapes — one drawn early, one faded in later beneath a highlight — is what this prevents: the same object is drawn twice, and the first copy stays under the second for the rest of the scene. ## Labels, clipping and the frame edge A label is drawn beside or above its mark rather than inside it, and everything is clipped to the plot plus the clear air just outside it — enough for a marker standing on an edge, not enough for the words naming it. A mark on a domain boundary therefore has its label pushed into that margin, taking the position that escapes the picture by least. A labelled point at the end of a series wants the domain to run past the last reading rather than stop on it, which is room for a label rather than padding for its own sake. Marks are spaced so that nothing overlaps, checked in the domain actually declared rather than the one the data suggests. Labels take the best free position in a ring around their mark, keeping off every other mark's ink as well as every other label, and step out to a second ring with a leader line back when the near positions are taken. Where nothing is free they overlap as little as they can — a label that is hard to read beats one that is not there, and a plot where that is happening is a plot with too much on it. ## Where a series label lands A label is placed against its mark's anchor, and for `line`, `polygon`, `scatter` and `bar` that anchor is the *mean of the readings*, not a point on the mark. A series that runs flat and then jumps puts its mean in empty space; two series over the same x values put both means at the same x. The layout will move the words clear and draw a leader back to the nearest ink, but they still name the series from wherever the mean fell. Where that reads badly, the series is named by a labelled `point` at a real vertex, chosen where the series are furthest apart, and the series itself carries no `label:`. ## Ticks the argument reads `!show background` removes grid, axes and ticks together, and is the frame for a picture with no numeric axis to read. Short of that, ticks stay wherever a sentence asks the reader to read a value — comparing two rates, locating an intersection, introducing a function. A claim that *it falls by the same amount each step* cannot be checked without the scale, so the rule is audited against the paragraphs actually written rather than in the abstract. Where the numbers are not part of the explanation, `!show ticks` drops them and `axis.label` names the values that are: it puts a name where the tick number would have gone, with `line` ruling it across the plot. ## One hue, one meaning A hue means one thing for the length of a document. Teal as the standout reading in one environment cannot be the reference line in the next, and grey as the reference in one is grey as the reference in all of them. A colour is checked against every other environment in the document, not only against the plot being written. An `axis.label` is chrome — it stands in for the tick it replaces — and keeps the neutral it defaults to unless its hue is doing semantic work, such as matching the one series it belongs to. A spare colour reached for so the name stands out puts a control into the register of the data, next to whatever the data hues already mean. --- # Parameters & cues The range criterion: a change belongs to a parameter when one sentence holds across its whole range, and to a cue otherwise. ## The range criterion A `parameter` and a `cue` both produce change: a parameter gives the change to the reader, a cue keeps it on the author's timeline. The criterion between them is the range. A change belongs to a parameter when one sentence remains true across the parameter's whole range, and to a cue otherwise. ## A parameter as a claim about its range `parameter(var: k; range: [0, 5])` asserts that the narrative holds anywhere in `[0, 5]`, since the reader can reach any value in it. The prose therefore describes the range rather than a point. - **Holds across the range:** *"At degree one the fit misses the curvature; past three it memorises the data."* - **Does not:** *"The fit passes close to every point"* — true at one setting and false at most others. Where a single value supports the sentence, that value is the author's and a cue expresses it. The criterion restricts the degrees of freedom a document exposes to those its argument covers. ## Choosing between cue and !cue.to - A mark that arrives is wrapped: `cue{ … }(in: 2)`. - A value that changes carries a keyframe micronode: `x-domain: [0, 5] !cue.to{[0, 10]}(at: 2)`. - A mark doing both is wrapped and carries a chain. `Motion` covers the anchors and durations both forms take. ## Auditing an existing document The criterion applies to documents already written: each parameter is moved to both ends of its range and the scene reread. Prose that is then false indicates either a sentence to rewrite or a parameter that should have been a cue. --- # Reading computed values in prose A number the canvas computes is read from the construct with a {{…}} expression rather than typed into the prose. ## Computed numbers in prose A number produced by a `fit`, an `integral`, a curve or a parameter is read from the construct rather than typed into the prose. A typed number is correct at the moment it was read off the screen and at no other: any edit to the data, change of model, or movement of a slider leaves the sentence asserting what the adjacent picture contradicts. ## Reading one in prose With a `{{…}}` expression naming the construct and the member. It works the same in prose and in any `rich-text` attribute, so a sentence and a mark's own label quote the same number: ```chalk Right now $k$ is {{#k.value}}, and the band holds {{round(100 * #band.value, 1)}}% of the distribution. ``` The expression does the arithmetic, and the `text functions` do the rest of the shaping — `!format:comma{ {{#n.value}} }` for separators. In attribute position a reference is written *bare* only where the attribute's declared type is `reference` (`fit(of: #points)`); in a `rich-text` one it is the group again. `Derived attributes` lists what each construct publishes. ## Why the compiler cannot freeze one Reading a value that moves at render time makes the surrounding expression **bound**: the compiler evaluates everything else in it and ships the rest as a tree, evaluated by the renderer every frame — which is what keeps a live number from ever being frozen into a sentence that would later contradict the picture beside it. ```chalk The fitted slope is {{#f.slope}}. ``` A dataset column reads plainly, with no such deferral, because a dataset is immutable: nothing can replace it and no cell depends on a varying value, so there is nothing for the expression to wait on. ## Rounding a live number - A bare reading shows about four significant figures. Too many read as spurious precision; too few print two distinct quantities identically, so a number worth quoting is usually worth rounding — `{{round(#f.slope, 2)}}`. - Round for the claim being made, not for the width of the number: a slope quoted to two decimals asserts that the second decimal means something. - A live value and a typed approximation in one sentence — *about 0.34* beside the reading itself — reproduce the disagreement the mechanism prevents. What the canvas does *not* compute is covered in `Checking a document's claims`. --- # Checking a document's claims Every number derived, every output quoted from a run, every external fact checked or cut — and the source cited. ## Numbers the author works out `Reading computed values in prose` covers the numbers the canvas computes. Every other number in a document — a worked example's arithmetic, a step count, a trace through an algorithm, the readings a plot is drawn from — is worked out before it is written. A number that looks plausible and a number that was derived read identically on the page, and the reader has no way to tell them apart, which is exactly why the burden sits with the author. ## Output quoted from a run A code environment's output is authored rather than executed — see `Code environment execution model` — and that puts the whole burden here: the comment or chunk stating what an interpreter prints is quoted from a run that actually happened. The same holds for a `runnable` environment, where a reader can compare the authored output against the real one in a single click. ## Facts checked or cut Historical claims, attributions and named results are checked against a source, or they come out of the document. Hedging an unchecked claim into safety — *often attributed to*, *is said to* — keeps the assertion and removes the accountability for it. Values quoted from the world are looked up rather than recalled: a coordinate on a `canvas.geo` written from memory puts a marker in the sea, and it is the one error a compiling document displays confidently. ## cite and the reference list A source the document draws on is declared with `cite` beside the scenes and pointed at from the prose with `!cite`. The reference list is generated from the declarations and numbered in citation order, so a citation of a source that was never declared is a compile error rather than a gap in the list, and a declared source nothing cites never appears. `locator:` carries where in the source the claim is — a page, a section, a figure. ```chalk cite#fs1969( authors: Fellegi, I. P. and Sunter, A. B. year: 1969 title: A theory for record linkage container: Journal of the American Statistical Association pages: 64(328), 1183–1210 doi: 10.1080/01621459.1969.10501049 ) scene{ The comparison vector and the likelihood ratio built from it are Fellegi and Sunter's !cite:#fs1969(locator: p. 1187), and the decision rule follows from them directly. } ``` A figure taken from a paper, a dataset with an origin, and a result quoted rather than derived are the three places a citation is not optional. --- # Code environment execution model A code environment shows a procedure. Its output is authored; a runnable environment may also be run by the reader, but nothing it prints produces canvas content. ## Execution never authors the canvas Nothing in a `canvas.code` environment produces canvas content. By default nothing executes at all; a `runnable` environment lets the reader run the visible text in a disposable sandbox, and what it prints lands in the environment's console — never on the canvas. No code in an environment produces a plot, feeds a graph, or generates data; the canvas beside it is authored separately. ## No kernels, environments or execution state An internote therefore carries no kernel, environment, package versions or execution state, and a reader opening a five-year-old internote sees what its author saw. A runnable environment keeps that promise: its sandbox exists for the press of Run and is gone after, and nothing the document shows depends on what one printed. A graph is authored in Chalk and states what its author meant, rather than what a script emitted on the day it last ran. ## Writing the output as a chunk or a comment A result the reader should see is authored: a comment, or a later chunk stating what an interpreter would print. It is content like the prose and carries the same obligation to be true — quoted from a run rather than predicted, as `Checking a document's claims` has it. ```chalk canvas.code{ lines{ z, log_c = np.polyfit(log_area, log_species, 1) } cue.type{ lines{ print(z) # 0.34 } }( in: 1 ) }( language: python ) ``` ## Splicing one source into both halves Neither half computes the other, so agreement between them is authored. `{{ }}` splices a shared value into both — a `@data` column reaches a literal code body as it reaches prose — and a number appearing in both the code and the argument is declared once as a `@let` constant. ```chalk @data#survey:galapagos-plants-1973.csv scene{ The areas come from the attached survey. }[ canvas.code{ lines{ area = np.array([{{#survey.area}}]) } }( language: python ) ] ``` --- # Data provenance A canvas shows data its author has seen. Reader-supplied data arrives by forking, with authorship attached. ## @data as the only entry point Data enters a document through `@data`, inline or attached as a CSV, both forms written into the source by the author. Reader-uploaded data does not enter a published internote. ## Authorship of reader-supplied data A canvas showing data its author has not seen has no author: the prose can make no claim about it and falls back on generic captions — *your data may show…*, *notice any outliers…* — the failure described in `Prose and canvas state`. The reasoning matches the range criterion: an author can describe a range they chose, and cannot describe rows they have never seen. ## Forking and the @data declaration Reader data enters by forking rather than by upload: the `@data` declaration is replaced and the narrative rewritten against the new rows, which restores an author to the document. ## Three obligations on the author - The data is read before it is described. Claims in the prose are claims about these rows. - The attached file is the data rather than a rounded excerpt, so a reader checking a number finds it. - Transformation happens upstream. `@data` has no computed columns, filters or aggregates. Rows belong in a `@data` block even where they are read once, which is a legibility rule as much as a provenance one — `Source style` covers it. --- # Source style The conventions that keep a .chalk file legible to the next person editing it by hand. ## Multiline nodes and multiline attribute groups A `.chalk` file is read and edited by hand, and the two forms of an `attribute group` are chosen to match the node carrying them. A node written entirely on one line takes semicolons. A node whose body or detail spans lines takes a group that spans lines too, one attribute per line — including where there is only one attribute, so that the closing brackets of a nested construct always read the same way. ```chalk curve(f: sin(x); colour: blue; dashed) cue{ curve(f: sin(x); colour: blue) }( in: 1 ) ``` ## Bare booleans, written last Booleans take the `bare-word shorthand` — `runnable`, `dashed`, `!fill`, `!show ticks` — never `: true` or `: false`. They are written last in every group, the `document header` included, so a group reads as values and then flags. ## Spaced names in attribute groups A hyphenated attribute name is written with spaces inside a group — `x domain`, `y axis label`, `in duration`, `header row`, `highlight rows`, `show background` — which reads as English in a group that is already one key per line. It applies to *keys* only: a binding carries the canonical spelling through to the renderer, so `#plot.x-domain` keeps its hyphen and `#plot.x domain` resolves to nothing. ## Seconds and milliseconds A duration of a second or more is written in seconds — `1.4s`, `2s`, `2.9s` — and anything shorter in milliseconds — `400ms`, `900ms`. Both suffixes are accepted wherever a time is written; the convention exists so a scene's timings can be compared down a column without converting them first. ## Bulk data and repeated expressions Rows of data live in a `@data` block, even where they are read once. A hundred readings written inline turn the environment holding them into a wall of digits, and the structure of the graph disappears behind its own numbers; the block is also where a reader looks to find what a document's numbers actually are. Columns bind where they are read — one column per axis on `scatter`, `line` and `polygon`. A long expression, and anything written more than once, is declared in `@let` and spliced back with `{{name}}`. That is also what keeps a number appearing in two places — a curve and the code beside it — from being edited in one of them. ```chalk @data#samples{ x | y 0.31 | 0.18 0.52 | -0.09 -0.66 | 0.76 } @let( surface: exp(-(x^2 + y^2) / 2) / (2 * pi) ) scene{ … }[ canvas.graph{ heatmap(expression: {{surface}}; colour: temperature) scatter(x: #samples.x; y: #samples.y; colour: grey) } ] ``` ## What a comment is for `//` at the start of a line keeps it out of the output and `@ignore` does the same for a block. The comment worth writing is the one saying where a constant came from — the source of a rate, the reason for a cutoff, the year a survey was run — which is the one fact the source cannot recover on its own. --- # The final pass Two passes over a finished draft: whether it is the right document, and whether it holds together. ## Compilation as the smaller half Everything in this section compiles. A file can satisfy the grammar exactly and still be the wrong document: two scenes nobody asked for, a canvas that decorates the argument rather than carrying it, a number typed in beside the mark that computes it. So the pass that catches those runs first, and the mechanical one after it. ## The first pass: the right document - It teaches the topic the header's `title` names, and nothing was added that the topic did not call for. - Every scene earns its place — none exists for variety, for length, or to use a construct the document had not used yet. - Every construct is there because the content needed it. - The headings alone are enough to navigate by, and none of them gestures where it could name. - Each detail reads on its own, full screen, with no narrative beside it. - Every number was computed and every external fact checked against a source or cut. ## The second pass: the mechanical checks - It compiles, and every construct it uses is one the reference documents. - Every `===` has a cue anchored to it — the compiler refuses an anchor past the last marker, and says nothing about a marker nothing fires on. - The first scene opens on prose, and the headings use more than one level. - No pronoun is left pointing at a sentence that became a heading. - Every multiline node carries a multiline attribute group. - No `: true` or `: false`; booleans last; hyphenated keys spaced. - Durations of a second or more are written in seconds. - No `size` or `width` except where the attribute is the geometry. - Each mark is declared once, with highlights and spotlights nested inside the cue that brought it in. - Domains are omitted where a shape has to be true and authored where the layout fills the frame, and neither is padded past the data. - Ticks match what the prose asks the reader to read, and each hue means one thing across the whole document. - Every `definition` carries a term that stands alone in a glossary, and every `note` is an aside rather than the next step of the argument. - Maths is LaTeX everywhere LaTeX is legal. - Bulk rows are in `@data` and repeated expressions in `@let`. - Every computed number in the prose is read with `{{#id.member}}` rather than typed. > The editor compiles as it goes and marks what it rejects, so the mechanical half of this list is answered by the source panel. The first half is not answered by anything, which is why it is first. --- # Attribute types Every value you write is text until the attribute it lands in parses it. Each type below is one way of reading that text — what it accepts, and how it is written. A value its attribute cannot parse is an error at compile time, with the reason named: nothing is silently converted or dropped. ## Text ### string Any text. - Accepts: `Any text` ```chalk hello Introduction to Graphs ``` ### rich-text Inline Chalk content — formatted text, `$maths$`, links, and inline elements all render. - Accepts: `Any inline Chalk` ```chalk local maximum the mean $\mu$ ``` ### single-char Exactly one alphabetical character. - Accepts: `Single letter` ```chalk x t ``` ## Numbers ### number A numeric literal. `class` decides whether a fractional part is allowed AND the compiled shape — `integer` emits a JSON integer, `decimal` a float. - Accepts: `Integer`, `Decimal` ```chalk 3 -2.5 0.35 ``` ### parametised-number Number value that may reference parameters/expressions. - Accepts: `Number`, `Parameter-based expression` ```chalk 2 a 2*a+1 ``` ### ratio One share over another, written as a fraction — the first environment's share of a split over the second's. - Accepts: `a/b` ```chalk 3/2 1/1 ``` ## True or false ### boolean A true/false flag, matched in any case. In an attribute group the name alone means true — `curve(dashed)` — and `!` before it means false: `curve(!dashed)`. The negated form is how you turn off an attribute that defaults to true. - Accepts: `true`, `false`, `yes`, `no` ```chalk true no ``` ## Colour ### colour-list Comma-separated colour names: `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `neutral`, `white`, `black`. - Accepts: `Comma-separated colour names` ```chalk blue, teal, orange, grey ``` ### category-colours A categorical legend, written category then hue: `Labor red, Coalition blue`. Categories the list does not name take the next unused hue, so nothing in the data silently vanishes. - Accepts: `Comma-separated category-then-hue pairs` ```chalk Labor red, Coalition blue Liberal red, Conservative blue, New Democratic orange ``` ## Lists ### string-list Comma-separated list of strings. - Accepts: `Comma-separated text values` ```chalk Chrome, Safari, Firefox ``` ### number-list Comma-separated list of numbers. - Accepts: `Comma-separated integers or decimals` ```chalk 65, 19, 4, 12 1.5, 2.0, 3.7 ``` ### parametised-number-list Comma-separated numbers, each of which may be a parameter expression. - Accepts: `Comma-separated numbers or expressions` ```chalk 1, 2, 4 a, 2*a ``` ### histogram-bins A histogram's bins: how many, or where their edges fall. One number is a count — `12`, or parametised, `k`, so a slider re-bins the sample — and it needs readings to bin. Two or more are the edges themselves, in order, one more than there are bars, and may be unequal. A single edge is not a grid, so a lone number is never read as one. - Accepts: `A count: a number or parameter expression`, `Comma-separated bin edges` ```chalk 12 k 0, 10, 20, 30 ``` ### series One axis of a multipoint mark (`x:`/`y:` on scatter, line, polygon, bar): comma-separated entries, each a number, a parameter expression, or a name — or a dataset column bound whole, `#islands.area`. The axis decides how an entry reads: numbers and expressions on a numeric axis, names on a categorical one. The two axes pair by position. - Accepts: `Comma-separated numbers, expressions or names`, `A dataset column: #id.column` ```chalk 1, 2, 4 k, 2*k, 4*k North, South, East #islands.area ``` ### index-list Comma-separated indices and ranges. Letters map to positions (A=1). - Accepts: `Single index`, `Comma-separated list`, `Range` ```chalk 1,3,5 1-4 A,C-E ``` ### cell-list Spreadsheet-like cell references and ranges. - Accepts: `A1`, `A1,B2`, `A1-C3` ```chalk A1,B2 C3-C5 ``` ## Points and paths ### parametised-coordinate-list Coordinate pairs with optional parameterized values. - Accepts: `(x,y) pairs` ```chalk (0,0),(1,1) (a,2),(b,3) ``` ### angle-arm One arm of an `angle`, in either of the two ways an arm is named: a point it runs through, or a bearing in degrees anticlockwise from the positive x axis. A point announces itself with its parentheses and its comma, so a bearing that happens to open with a bracket — `(a + b) * 2` — is still read as the expression it is. Either form may be parametised, so an arm can follow a reader's slider. - Accepts: `(x, y)`, `Degrees` ```chalk (4, 1) 30 (cos(t), sin(t)) ``` ### graph-transform A map of a graph's plane, written in the notation that plane is mapped in. The ordinary plane takes a 2×2 matrix as its two ROWS, the way one is written on paper: `(1, 1), (0, 1)` is (x, y) ↦ (x + y, y), and its columns are where the basis vectors land. Entries may be parametised. The argand plane takes a function of `z` instead, read as any other complex expression. A matrix announces itself with its rows, so a complex map that opens with a bracket — `(1 + i) * z` — is still read as the expression it is. - Accepts: `Matrix rows: (a, b), (c, d)`, `A function of z` ```chalk (0, -1), (1, 0) (1, k), (0, 1) z^2 (z + 1/z) / 2 ``` ### curve-path The curve to follow: a `#id` reference to a curve or fit, or an expression in x given directly. - Accepts: `#id`, `Expression in x` ```chalk #flight x^2 - 1 ``` ### geo-coordinate A place on the earth, written LATITUDE FIRST: `48.85, 2.35` is Paris. Either half may be a parametised expression, so a place can ride a slider or a keyframe. - Accepts: `lat, lon` ```chalk 48.85, 2.35 35.68, 139.69 0, lon ``` ### geo-anchor A place, in either of the two ways a geographic environment names one: a `geo-coordinate`, or `#id` naming a marker the same environment already draws. The reference form is what lets a map that pins Tokyo also be framed on Tokyo without writing the coordinate twice. - Accepts: `lat, lon`, `#id of a marker in the same environment` ```chalk 48.85, 2.35 #tokyo ``` ### diagram-point A place on a diagram environment's content frame, as fractions: `0.62, 0.31` is 62% across and 31% from the top. The origin is the TOP LEFT and y runs DOWN — the way a position is read off an image — unlike the graph's y-up coordinates. Both halves are plain numbers between 0 and 1. - Accepts: `u, v — fractions of the frame, 0 to 1` ```chalk 0.62, 0.31 0.5, 0.5 ``` ### diagram-anchor A place on the frame, in either of the two ways a diagram environment names one: a `diagram-point`, or `#id` naming a box or pin the same environment already draws. The reference form is what lets a frame that labels the nucleus also be focused on the nucleus without writing the fraction twice. - Accepts: `u, v`, `#id of a box or pin in the same environment` ```chalk 0.62, 0.31 #nucleus ``` ### diagram-ref `#id` naming a box or pin in the same diagram environment — an arrow's endpoints. Nothing else is accepted, and an id no box or pin declares is a compile error. - Accepts: `#id of a box or pin in the same environment` ```chalk #pcr #nucleus ``` ## Ranges and spacing ### interval An ascending inclusive range. A descending range is a compile error. This is a VALUE FORM, not `@for` syntax — any attribute whose type admits an interval accepts one, and an interval may be a list item, which is how an attribute comes to accept `1, 2, 3, 7..11`. - Accepts: `from..to` ```chalk 0..6 1, 2, 3, 7..11 ``` ### parametised-interval Interval that can include parameter expressions in bounds. - Accepts: `Interval-like syntax with expressions` ```chalk [-10, 10] [a, b] ``` ### axis-value A place on a graph axis: a number or parameter expression on a numeric axis, a date literal on a dates axis, or a name on a categorical one. On the polar plane an angle takes `60deg`. - Accepts: `A number or expression`, `A name on a categorical axis` ```chalk 0 2 * k 2024-03 Wed 90deg ``` ### axis-interval An interval on a graph axis — a domain, a band, a brace, a `!cue.zoom` window. Each end is a number or expression on a numeric axis, a date on a dates axis, or a name on a categorical one; an end left empty is unbounded. - Accepts: `[start, end] with numbers or expressions`, `[start, end] with names on a categorical axis` ```chalk [0, 10] [a, b] [Tue, Thu] [0, ] ``` ### axis-scale How a graph axis places a value: `linear`, `log` (v at log₁₀ v) or `log2`. A transform of numbers, refused beside names, dates or angles. - Accepts: `linear`, `log`, `log2` ```chalk linear log ``` ### axis-format What a graph axis holds: `auto` (decided from the marks), `numbers`, `radians`, `degrees`, `dates`, a preset series (`months`, `weekdays`), a written series of names, or a dataset column whose distinct cells are the names. A series makes the axis categorical. - Accepts: `auto`, `numbers, radians, degrees, dates`, `months, weekdays`, `Comma-separated names`, `A dataset column: #id.column` ```chalk auto radians months Red, Blue, Yellow #sales.region ``` ### grid-step Grid spacing mode. - Accepts: `auto`, `Positive integer` ```chalk auto 2 5 ``` ## Expressions ### parametised-expression Math expression that may include parameter variables. - Accepts: `Expression string` ```chalk x^2 + 2x + 1 sin(a*x) ``` ### parametised-surface-expression Math expression over the plane — the one place `y` is legal alongside `x` — that may also reference parameters. - Accepts: `Expression in x and y` ```chalk x^2 + y^2 sin(x) * cos(y) + a ``` ## Timing and animation ### duration Animation duration value in milliseconds (internally normalized). - Accepts: `Integer duration` ```chalk 500 750 1200 ``` ### duration-auto Either explicit duration or auto-calculated timing. - Accepts: `auto`, `Integer duration` ```chalk auto 1200 ``` ### cue-anchor When something fires, in scene time: a step index, an absolute time, or a step with a time offset. - Accepts: `Step index`, `Absolute time`, `step + offset` ```chalk 2 1500ms 2 + 500ms ``` ### optional-cue-anchor Nullable cue anchor — null means the cue never fires on its own. - Accepts: `null`, `Step index`, `Absolute time`, `step + offset` ```chalk null 2 4 + 1.5s ``` ## References ### reference An `#id` pointer, compiled to a binding the renderer resolves. The attribute holds a pointer and nothing more. `targets` names node or micronode types, or `@data` for a dataset. - Accepts: `#id`, `#id.member` ```chalk #flight #fit.slope ``` ## Fixed choices ### plane A graph's plane — what a coordinate on its marks is: `x` a number line, `xy` the ordinary plane, `argand` the complex plane, `polar` (r, theta). - Accepts: `x`, `xy`, `argand`, `polar` ```chalk xy argand polar ``` ## Expressions ### curve-expression A curve's function of its plane's independent axis: an expression in `x` on the ordinary plane, in `theta` on the polar one. Parameters are the other letters; `60deg` writes an angle in degrees. `min(a, b)`, `max(a, b)` and `clamp(x, lo, hi)` are how a function defined in pieces is written as one curve. - Accepts: `An expression in x`, `An expression in theta (polar)` ```chalk x^2 a * sin(b * x) 1 + cos(theta) min(2x, 10) ``` ### parametric-expression One coordinate of a curve that is a position in `t`: an expression in `t`, the other letters parameters. - Accepts: `An expression in t` ```chalk cos(t) a * sin(2t) ``` ### complex-expression A complex number, as an expression: a real term, an imaginary term with `i`, or any expression of them — `^` the principal power; `abs`, `arg`, `conj`, `re`, `im` and the ordinary functions. Single letters are parameters, read as complex. - Accepts: `A complex literal: 3 + 2i, -i, 4`, `An expression in i and the parameters` ```chalk 3 + 2i (3 + 2i)^2 sqrt(13) * e^(i * a) conj(z) ``` ### complex-parametric-expression A curve on the argand plane as z(t): a complex expression in `t`. - Accepts: `A complex expression in t` ```chalk e^(i * t) sqrt(13) * e^(i * t) t + i t^2 ``` ## Lists ### complex-series A series of complex numbers: complex expressions written out, comma-separated (a bare number a real), or a column of complex literals bound whole. - Accepts: `Comma-separated complex expressions`, `A dataset column: #id.column` ```chalk 1 + i, 2 - i, -3i, 4 #roots.z ``` --- # Packs A pack is a named collection of ready-made constructs — a distribution, a boxplot lane, a dataset — written once and imported by name. Its constructs are called under that name, so where one came from is visible at every use, and its attributes are typed, so a value it cannot accept is an error where you wrote it rather than a surprise in the drawing. Import a pack once, near the top of the internote, then call its constructs with the pack’s name in front. What a pack draws is ordinary chalk: the compiled document is the same as if the nodes had been written out by hand. ```chalk @use:statistics canvas.graph{ statistics.normal(mu: 0; sigma: 1) }( x domain: [-4, 4] y domain: [0, 0.45] ) ``` - Qualification is mandatory — there is no unqualified import, so two packs can never collide. - A different prefix is given with `@use(pack: statistics; as: st)`, and it REPLACES the pack’s own name. - Only what a pack publishes can be reached; the rest is its own workings. - A pack cannot see anything in your document — its constants, ids and templates are invisible to it, and yours to you. ## economics Imported with `@use:economics`. ### economics.supply-demand Supply and demand crossing, with the equilibrium marked and dropped to both axes. - `a` — The supply curve's price intercept, in $p = a + bq$. - Type: `parametised-number` - Default: `1` - `b` — The supply curve's slope, in $p = a + bq$. - Type: `parametised-number` - Default: `1` - `c` — The demand curve's price intercept, in $p = c - dq$. - Type: `parametised-number` - Default: `11` - `d` — The demand curve's slope, in $p = c - dq$. - Type: `parametised-number` - Default: `1` - `qmax` — The quantity both curves are drawn out to. - Type: `parametised-number` - Default: `10` - `label` — The equilibrium point's label. - Type: `inline` - Default: `$E$` - `supply-label` — The supply curve's label. - Type: `inline` - Default: `$S$` - `demand-label` — The demand curve's label. - Type: `inline` - Default: `$D$` - `price-label` — The dashed segment's label on the price axis. - Type: `inline` - Default: `$p^*$` - `quantity-label` — The dashed segment's label on the quantity axis. - Type: `inline` - Default: `$q^*$` - `colour` — The equilibrium point and the dashed segments' colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `grey` - `supply-colour` — The supply curve's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `blue` - `demand-colour` — The demand curve's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `red` ### economics.demand-shift Demand shifted, drawn dashed over the original with the new equilibrium marked: `by` is how far up the price axis the whole curve moves, positive for an increase in demand. - `a` — The original supply curve's price intercept, in $p = a + bq$. - Type: `parametised-number` - Default: `1` - `b` — The original supply curve's slope, in $p = a + bq$. - Type: `parametised-number` - Default: `1` - `c` — The original demand curve's price intercept, in $p = c - dq$. - Type: `parametised-number` - Default: `11` - `d` — The original demand curve's slope, in $p = c - dq$. - Type: `parametised-number` - Default: `1` - `by` — How far up the price axis demand shifts, positive for an increase in demand. - Type: `parametised-number` - Default: `3` - `qmax` — The quantity the shifted curve is drawn out to. - Type: `parametised-number` - Default: `10` - `label` — The shifted demand curve's label. - Type: `inline` - Default: `$D'$` - `point-label` — The new equilibrium point's label. - Type: `inline` - Default: `$E'$` - `colour` — The shifted curve and new equilibrium point's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `orange` ### economics.supply-shift Supply shifted, the same way: `by` positive is a rise in costs, which moves the curve UP and the equilibrium quantity down. - `a` — The original supply curve's price intercept, in $p = a + bq$. - Type: `parametised-number` - Default: `1` - `b` — The original supply curve's slope, in $p = a + bq$. - Type: `parametised-number` - Default: `1` - `c` — The original demand curve's price intercept, in $p = c - dq$. - Type: `parametised-number` - Default: `11` - `d` — The original demand curve's slope, in $p = c - dq$. - Type: `parametised-number` - Default: `1` - `by` — How far up the price axis costs rise, which moves the curve up and the equilibrium quantity down. - Type: `parametised-number` - Default: `2` - `qmax` — The quantity the shifted curve is drawn out to. - Type: `parametised-number` - Default: `10` - `label` — The shifted supply curve's label. - Type: `inline` - Default: `$S'$` - `point-label` — The new equilibrium point's label. - Type: `inline` - Default: `$E'$` - `colour` — The shifted curve and new equilibrium point's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `purple` ### economics.surplus Consumer and producer surplus as the two triangles they are: everything between the curve and the equilibrium price, either side of it. Shaded polygons rather than `integral`s, because an integral shades down to the AXIS and a surplus stops at the price. - `a` — The supply curve's price intercept, in $p = a + bq$. - Type: `parametised-number` - Default: `1` - `b` — The supply curve's slope, in $p = a + bq$. - Type: `parametised-number` - Default: `1` - `c` — The demand curve's price intercept, in $p = c - dq$. - Type: `parametised-number` - Default: `11` - `d` — The demand curve's slope, in $p = c - dq$. - Type: `parametised-number` - Default: `1` - `consumer-label` — The consumer surplus triangle's label. - Type: `inline` - Default: `$CS$` - `producer-label` — The producer surplus triangle's label. - Type: `inline` - Default: `$PS$` - `consumer-colour` — The consumer surplus triangle's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `teal` - `producer-colour` — The producer surplus triangle's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `orange` ### economics.tax-wedge A per-unit tax: supply shifted up by the tax, the price the buyer pays and the price the seller keeps ruled across, and the deadweight loss between them as its own triangle. The wedge is the tax itself — the two rules are exactly `t` apart, because the seller's price is the buyer's less the tax and nothing here rounds. - `a` — The supply curve's price intercept before the tax, in $p = a + bq$. - Type: `parametised-number` - Default: `1` - `b` — The supply curve's slope, in $p = a + bq$. - Type: `parametised-number` - Default: `1` - `c` — The demand curve's price intercept, in $p = c - dq$. - Type: `parametised-number` - Default: `11` - `d` — The demand curve's slope, in $p = c - dq$. - Type: `parametised-number` - Default: `1` - `t` — The per-unit tax, the exact gap between what the buyer pays and the seller keeps. - Type: `parametised-number` - Default: `2` - `qmax` — The quantity the curves are drawn out to. - Type: `parametised-number` - Default: `10` - `label` — The taxed supply curve's label. - Type: `inline` - Default: `$S + t$` - `buyer-label` — The ruled line's label at the price the buyer pays. - Type: `inline` - Default: `$p_b$` - `seller-label` — The ruled line's label at the price the seller keeps. - Type: `inline` - Default: `$p_s$` - `loss-label` — The deadweight loss triangle's label. - Type: `inline` - Default: `$DWL$` - `colour` — The taxed supply curve and the two price rules' colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `grey` - `loss-colour` — The deadweight loss triangle's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `red` ### economics.budget-line A budget line: everything an income of $m$ buys at two prices, with both intercepts dotted. Inside it is affordable, beyond it is not. - `m` — The income the budget line is drawn for. - Type: `parametised-number` - Default: `12` - `p1` — The price of the good on the x axis. - Type: `parametised-number` - Default: `2` - `p2` — The price of the good on the y axis. - Type: `parametised-number` - Default: `3` - `label` — The budget line's own label. - Type: `inline` - Default: `` - `x-label` — The x-intercept's label. - Type: `inline` - Default: `$m/p_1$` - `y-label` — The y-intercept's label. - Type: `inline` - Default: `$m/p_2$` - `colour` — The line and both intercept points' colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `blue` ### economics.ppf A production possibility frontier — the concave arc of everything an economy can make at once — with one efficient point on it dropped to both axes, which is the trade-off the diagram exists to show. A quarter ellipse, so the frontier bows outward and the opportunity cost of the first good rises as more of it is made. `at` is how far round the arc the point sits, as a fraction. `filled` shades everything inside it — the attainable set. The region closes through the origin, because that is what "attainable" means here: making less of both is always possible. - `x-max` — The frontier's intercept on the x axis, the most of that good the economy can make alone. - Type: `parametised-number` - Default: `10` - `y-max` — The frontier's intercept on the y axis, the most of that good the economy can make alone. - Type: `parametised-number` - Default: `8` - `at` — How far round the arc the marked point sits, as a fraction from 0 to 1. - Type: `parametised-number` - Default: `0.4` - `filled` — Shade the attainable set inside the frontier. - Type: `bool` - Default: `false` - `label` — The frontier curve's label. - Type: `inline` - Default: `$PPF$` - `point-label` — The marked point's label. - Type: `inline` - Default: `$A$` - `colour` — The frontier curve's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `blue` - `point-colour` — The marked point and its dropped segments' colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `orange` ## geometry Imported with `@use:geometry`. ### geometry.triangle-abc A triangle from its three vertices, with the corners lettered. The letters are what this is for: `polygon` draws the outline in one line already, and then every figure wants three dots and three letters placed beside them without landing on the sides. Each letter is a `point`'s label, so the graph's own label layout keeps it clear of the ink. - `ax` — Corner A's x coordinate. - Type: `parametised-number` - Default: `0` - `ay` — Corner A's y coordinate. - Type: `parametised-number` - Default: `0` - `bx` — Corner B's x coordinate. - Type: `parametised-number` - Default: `4` - `by` — Corner B's y coordinate. - Type: `parametised-number` - Default: `0` - `cx` — Corner C's x coordinate. - Type: `parametised-number` - Default: `1` - `cy` — Corner C's y coordinate. - Type: `parametised-number` - Default: `3` - `a-label` — Corner A's label. - Type: `inline` - Default: `$A$` - `b-label` — Corner B's label. - Type: `inline` - Default: `$B$` - `c-label` — Corner C's label. - Type: `inline` - Default: `$C$` - `fill` — Shade the triangle's interior. - Type: `bool` - Default: `false` - `opacity` — The fill's opacity, from 0 to 1. - Type: `number` - Default: `1` - `colour` — The outline, fill and corner points' colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `blue` ### geometry.right-triangle A right-angled triangle standing on its corner, the square marked and each side labelled — the figure trigonometry is taught on. The legs are LENGTHS rather than opposite corners: `a` runs right from the corner and `b` runs up from it, so the right angle is where the two meet and cannot be put anywhere else. The sides are drawn as three segments rather than as one polygon because each one carries a label, and a label belongs to the side it names. - `a` — The length of the horizontal leg, running right from the right-angle corner. - Type: `parametised-number` - Default: `4` - `b` — The length of the vertical leg, running up from the right-angle corner. - Type: `parametised-number` - Default: `3` - `x` — The right-angle corner's x coordinate. - Type: `parametised-number` - Default: `0` - `y` — The right-angle corner's y coordinate. - Type: `parametised-number` - Default: `0` - `a-label` — The horizontal leg's label. - Type: `inline` - Default: `$a$` - `b-label` — The vertical leg's label. - Type: `inline` - Default: `$b$` - `c-label` — The hypotenuse's label. - Type: `inline` - Default: `$c$` - `colour` — The three sides and the right-angle mark's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `blue` ### geometry.pythagoras The Pythagoras figure: a right triangle with a square drawn outward on each of its three sides, each square labelled with its area. The hypotenuse's square is the one nobody wants to work out by hand. Its far corners are the near ones displaced by the hypotenuse turned a quarter turn — $(-a, b)$ becomes $(b, a)$ — which is why the figure comes out as four coordinates and not as a construction. - `a` — The length of the horizontal leg, running right from the right-angle corner. - Type: `parametised-number` - Default: `4` - `b` — The length of the vertical leg, running up from the right-angle corner. - Type: `parametised-number` - Default: `3` - `x` — The right-angle corner's x coordinate. - Type: `parametised-number` - Default: `0` - `y` — The right-angle corner's y coordinate. - Type: `parametised-number` - Default: `0` - `a-label` — The square on side $a$'s area label. - Type: `inline` - Default: `$a^2$` - `b-label` — The square on side $b$'s area label. - Type: `inline` - Default: `$b^2$` - `c-label` — The square on the hypotenuse's area label. - Type: `inline` - Default: `$c^2$` - `a-colour` — The square on side $a$'s colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `blue` - `b-colour` — The square on side $b$'s colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `green` - `c-colour` — The square on the hypotenuse's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `orange` - `colour` — The triangle's own sides and right-angle mark's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `neutral` ### geometry.regular-ngon A regular polygon of any number of sides, about a centre. Drawn side by side with `@for`, because a series is text the compiler reads in one piece and a loop generates elements rather than entries: the last side closes the shape because its far corner is the turn come all the way round. `turn` rotates the whole figure — a square at 45° stands on a corner, and at 0° it sits flat. It also derives its interior angle, so a lesson can state the number beside the figure without writing it twice: name the call and read `#id.interior`. The entry reads `sides` and nothing else, which is what lets it be derived at all — `sides` drives the `@for`, so it is a compile-time position that can never hold a value waiting on the reader, whereas `radius` and `turn` are parametised and so are not arithmetic the compiler can finish. - `sides` — How many sides the polygon has. - Type: `number` - Default: `6` - `radius` — The distance from the centre to each corner. - Type: `parametised-number` - Default: `3` - `cx` — The centre's x coordinate. - Type: `parametised-number` - Default: `0` - `cy` — The centre's y coordinate. - Type: `parametised-number` - Default: `0` - `turn` — The rotation of the whole figure, in degrees — a square at 45° stands on a corner. - Type: `parametised-number` - Default: `0` - `width` — The sides' line width. - Type: `number` - Default: `2` - `colour` — The sides' colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `purple` ### geometry.midpoint The point halfway between two others, lettered, with the segment it halves drawn behind it. The segment is drawn as its two HALVES so that `ticks` can mark them equal: one tick on each is the notation for "these two lengths are the same", and it is the whole reason a midpoint is worth marking rather than stating. - `x1` — The first end's x coordinate. - Type: `parametised-number` - Default: `0` - `y1` — The first end's y coordinate. - Type: `parametised-number` - Default: `0` - `x2` — The second end's x coordinate. - Type: `parametised-number` - Default: `4` - `y2` — The second end's y coordinate. - Type: `parametised-number` - Default: `2` - `ticks` — How many tick marks show on each half of the segment, marking them equal. - Type: `number` - Default: `1` - `label` — The midpoint's label. - Type: `inline` - Default: `$M$` - `colour` — The segment and midpoint's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `teal` ### geometry.perpendicular-bisector A segment, its midpoint, and the perpendicular bisector through it — the locus of the points equidistant from the two ends. The bisector runs `length` either side of the midpoint along the segment's normal, which is the segment turned a quarter turn and divided by its own length. Written out that division is the whole construct: it is what keeps the arms the length they were asked for however long the segment is. - `x1` — The segment's first end, x coordinate. - Type: `parametised-number` - Default: `0` - `y1` — The segment's first end, y coordinate. - Type: `parametised-number` - Default: `0` - `x2` — The segment's second end, x coordinate. - Type: `parametised-number` - Default: `4` - `y2` — The segment's second end, y coordinate. - Type: `parametised-number` - Default: `2` - `length` — How far the bisector runs either side of the midpoint. - Type: `parametised-number` - Default: `2` - `label` — The bisector's label. - Type: `inline` - Default: `` - `dashed` — Draw the bisector dashed. - Type: `bool` - Default: `true` - `colour` — The original segment and its midpoint's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `grey` - `bisector-colour` — The bisector and its right-angle mark's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `red` ### geometry.circle-arc An arc of a circle, from one bearing to another, both in degrees anticlockwise from the right. A parametric curve rather than a run of points: the arc is exact at every zoom, and there is no vertex count to pick. `filled` shades what the arc encloses on its own, which is the SEGMENT of the circle — the piece the chord between its ends cuts off. The slice from the centre is `sector`, because that region closes somewhere else. `circle-arc` rather than `arc` because a define may not take a library node's name, and `arc` is one — the great-circle overlay on the geo canvas. - `radius` — The circle's radius. - Type: `parametised-number` - Default: `3` - `from` — The bearing the arc starts at, in degrees anticlockwise from the right. - Type: `parametised-number` - Default: `0` - `to` — The bearing the arc ends at, in degrees anticlockwise from the right. - Type: `parametised-number` - Default: `90` - `cx` — The circle's centre, x coordinate. - Type: `parametised-number` - Default: `0` - `cy` — The circle's centre, y coordinate. - Type: `parametised-number` - Default: `0` - `width` — The arc's line width. - Type: `number` - Default: `2` - `filled` — Shade the segment the arc cuts off with its chord. - Type: `bool` - Default: `false` - `label` — The arc's label. - Type: `inline` - Default: `` - `colour` — The arc's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `teal` ### geometry.sector A sector: the arc between two bearings and the two radii that close it, with the angle at the centre marked. `filled` shades the slice. The arc is drawn here rather than handed to `circle-arc` for exactly that reason: a sector's region closes through the CENTRE, and an arc left to itself closes across its own chord — which is a different shape and a different piece of the circle. - `radius` — The circle's radius. - Type: `parametised-number` - Default: `3` - `from` — The bearing the sector starts at, in degrees anticlockwise from the right. - Type: `parametised-number` - Default: `0` - `to` — The bearing the sector ends at, in degrees anticlockwise from the right. - Type: `parametised-number` - Default: `90` - `cx` — The circle's centre, x coordinate. - Type: `parametised-number` - Default: `0` - `cy` — The circle's centre, y coordinate. - Type: `parametised-number` - Default: `0` - `width` — The arc and two radii's line width. - Type: `number` - Default: `2` - `filled` — Shade the sector. - Type: `bool` - Default: `false` - `label` — The angle at the centre's label. - Type: `inline` - Default: `$\theta$` - `show-value` — Show the angle's numeric value beside its label. - Type: `bool` - Default: `false` - `colour` — The sector's arc, radii and angle mark's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `teal` ### geometry.circle-radius A circle with its centre dotted and one radius drawn out to a bearing, labelled. `size` on the `circle` mark IS the radius, so the segment ends exactly on the circumference — which is the one thing a hand-written version of this figure tends to get wrong. - `radius` — The circle's radius. - Type: `parametised-number` - Default: `3` - `cx` — The circle's centre, x coordinate. - Type: `parametised-number` - Default: `0` - `cy` — The circle's centre, y coordinate. - Type: `parametised-number` - Default: `0` - `at` — The bearing the radius is drawn out to, in degrees anticlockwise from the right. - Type: `parametised-number` - Default: `40` - `label` — The radius segment's label. - Type: `inline` - Default: `$r$` - `filled` — Shade the circle's interior. - Type: `bool` - Default: `false` - `dashed` — Draw the radius segment dashed. - Type: `bool` - Default: `false` - `colour` — The circle outline and centre point's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `grey` - `radius-colour` — The radius segment's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `teal` ### geometry.tangent A tangent touching a circle at a bearing, with the radius to the point of contact and the right angle between them — the circle theorem, drawn. - `radius` — The circle's radius. - Type: `parametised-number` - Default: `3` - `at` — The bearing of the point of contact, in degrees anticlockwise from the right. - Type: `parametised-number` - Default: `55` - `length` — The tangent line's length, drawn half either side of the point of contact. - Type: `parametised-number` - Default: `3` - `cx` — The circle's centre, x coordinate. - Type: `parametised-number` - Default: `0` - `cy` — The circle's centre, y coordinate. - Type: `parametised-number` - Default: `0` - `filled` — Shade the circle's interior. - Type: `bool` - Default: `false` - `label` — The point of contact's label. - Type: `inline` - Default: `$T$` - `radius-label` — The radius segment's label. - Type: `inline` - Default: `$r$` - `colour` — The circle outline and centre point's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `grey` - `tangent-colour` — The tangent line, right-angle mark and point of contact's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `red` ### geometry.unit-circle-point The unit circle with one angle taken round it: the radius to the point, the cosine along the axis and the sine standing on it, each in its own colour. The two legs are what the figure is for — $\cos\theta$ is the distance across and $\sin\theta$ the distance up, and drawn in different colours they stop being two entries in a table. - `at` — The angle taken round the circle, in degrees anticlockwise from the right. - Type: `parametised-number` - Default: `55` - `radius` — The circle's radius. - Type: `parametised-number` - Default: `1` - `label` — The point's label. - Type: `inline` - Default: `$P$` - `angle-label` — The angle at the centre's label. - Type: `inline` - Default: `$\theta$` - `cos-label` — The cosine leg's label, the segment along the axis. - Type: `inline` - Default: `$\cos\theta$` - `sin-label` — The sine leg's label, the segment standing on the axis. - Type: `inline` - Default: `$\sin\theta$` - `colour` — The point, its radius and the angle mark's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `blue` - `cos-colour` — The cosine leg's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `orange` - `sin-colour` — The sine leg's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `green` ### geometry.parallel-cut Two parallel lines cut by a transversal, with the corresponding angles marked at both crossings — the figure every angle rule is read off. The lower line is $y = 0$ and the upper is $y = $ `gap`; the transversal crosses the lower one at the origin and the upper one `gap / tan` along, which is where the second angle is marked. Both angles are measured from the same direction and labelled the same, because that is the claim the picture makes. - `gap` — The vertical distance between the two parallel lines. - Type: `parametised-number` - Default: `3` - `angle` — The angle the transversal makes with the lines, in degrees. - Type: `parametised-number` - Default: `55` - `span` — How far each parallel line is drawn either side of the transversal. - Type: `parametised-number` - Default: `10` - `label` — Both marked angles' label. - Type: `inline` - Default: `$\theta$` - `colour` — The two parallel lines' colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `grey` - `cut-colour` — The transversal, its two marked angles and the two crossing points' colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `purple` ## physics Imported with `@use:physics`. ### physics.force One labelled force: an arrow of a given length, from a point, at a bearing. The whole of a free-body diagram is several of these at one point, which is why it is this rather than a construct taking four forces at once — a figure with three forces should not have to leave a fourth blank. - `magnitude` — The arrow's length, in graph units rather than newtons. - Type: `parametised-number` - Default: `2` - `bearing` — The arrow's direction, in degrees anticlockwise from the right. - Type: `parametised-number` - Default: `0` - `x` — The arrow's starting point, x coordinate. - Type: `parametised-number` - Default: `0` - `y` — The arrow's starting point, y coordinate. - Type: `parametised-number` - Default: `0` - `label` — The force's label. - Type: `inline` - Default: `` - `dashed` — Draw the arrow dashed. - Type: `bool` - Default: `false` - `width` — The arrow's line width. - Type: `number` - Default: `2` - `colour` — The arrow's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `red` ### physics.resolve A force with its two components drawn dashed beside it, and the right angle between them marked. `along` is the direction the components are taken in — 0 for the ordinary horizontal and vertical pair, the slope's angle for a block on a slope, which is the case the resolution exists for. The arithmetic is the point: the parallel component is $F\cos(\theta - \alpha)$ and the perpendicular one $F\sin(\theta - \alpha)$, and getting the sign of that difference right by hand is where the figure usually goes wrong. - `magnitude` — The force's length, in graph units rather than newtons. - Type: `parametised-number` - Default: `3` - `bearing` — The force's direction, in degrees anticlockwise from the right. - Type: `parametised-number` - Default: `55` - `along` — The direction the components are resolved in, in degrees — the slope's angle for a block on a slope. - Type: `parametised-number` - Default: `0` - `x` — The force's starting point, x coordinate. - Type: `parametised-number` - Default: `0` - `y` — The force's starting point, y coordinate. - Type: `parametised-number` - Default: `0` - `label` — The original force's label. - Type: `inline` - Default: `$F$` - `parallel-label` — The component along `along`'s label. - Type: `inline` - Default: `` - `perpendicular-label` — The component perpendicular to `along`'s label. - Type: `inline` - Default: `` - `colour` — The original force arrow's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `red` - `parallel-colour` — The parallel component's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `orange` - `perpendicular-colour` — The perpendicular component's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `teal` ### physics.incline A block on a slope: the ground, the incline, the block sitting on it and the angle at the foot. The block is a rectangle turned to lie on the slope, so its corners come out of the slope's own direction and normal rather than being written down — which is what a hand-drawn version of this figure never quite manages. `at` is how far along the slope it sits, as a fraction. - `angle` — The slope's angle to the ground, in degrees. - Type: `parametised-number` - Default: `30` - `base` — The horizontal length of the ground the slope rises from. - Type: `parametised-number` - Default: `6` - `at` — How far along the slope the block sits, as a fraction from 0 to 1. - Type: `parametised-number` - Default: `0.55` - `block` — The block's side length. - Type: `parametised-number` - Default: `1` - `x` — The foot of the slope's x coordinate. - Type: `parametised-number` - Default: `0` - `y` — The foot of the slope's y coordinate. - Type: `parametised-number` - Default: `0` - `label` — The angle at the foot's label. - Type: `inline` - Default: `$\theta$` - `colour` — The ground segment's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `grey` - `slope-colour` — The slope and its angle mark's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `blue` - `block-colour` — The block's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `orange` ### physics.pendulum A pendulum hanging from a pivot: the string at an angle FROM THE VERTICAL, the bob, the vertical it swings about, and the angle between them. - `angle` — The string's angle from the vertical, in degrees. - Type: `parametised-number` - Default: `25` - `length` — The string's length. - Type: `parametised-number` - Default: `4` - `bob` — The bob's size. - Type: `parametised-number` - Default: `14` - `x` — The pivot's x coordinate. - Type: `parametised-number` - Default: `0` - `y` — The pivot's y coordinate. - Type: `parametised-number` - Default: `5` - `label` — The angle between the string and the vertical's label. - Type: `inline` - Default: `$\theta$` - `length-label` — The string's label. - Type: `inline` - Default: `$l$` - `colour` — The pivot point and the vertical dashed line's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `grey` - `string-colour` — The string and its angle mark's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `blue` - `bob-colour` — The bob's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `orange` ### physics.projectile A projectile's flight: the trajectory, the apex, and the range, from a launch speed and an angle. Everything but the curve is a consequence of those two — the range is $u^2\sin 2\theta / g$ and the apex $u^2\sin^2\theta / 2g$ — so the markers land where the curve actually goes rather than where they were dragged. - `u` — The launch speed. - Type: `parametised-number` - Default: `20` - `angle` — The launch angle above the horizontal, in degrees. - Type: `parametised-number` - Default: `45` - `g` — The acceleration due to gravity. - Type: `parametised-number` - Default: `9.81` - `label` — The trajectory curve's label. - Type: `inline` - Default: `` - `apex-label` — The apex point's label. - Type: `inline` - Default: `$H$` - `range-label` — The range brace's label. - Type: `inline` - Default: `$R$` - `launch-label` — The launch velocity vector's label. - Type: `inline` - Default: `$u$` - `colour` — The trajectory curve, the range point and the brace's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `blue` - `apex-colour` — The apex point and its dropped segment's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `teal` - `launch-colour` — The launch velocity vector's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `red` ### physics.vt-displacement A velocity–time graph under constant acceleration, with the area under it shaded: the displacement, which is what the area IS. The line is a `curve` rather than a `line` so that `integral` can shade under it — the area is sampled from the curve itself, so it follows the line however the acceleration changes. - `u` — The initial velocity. - Type: `parametised-number` - Default: `2` - `a` — The constant acceleration. - Type: `parametised-number` - Default: `1.5` - `from` — Where the shaded area starts, along the time axis. - Type: `parametised-number` - Default: `0` - `to` — Where the line is drawn to, and where the shaded area ends. - Type: `parametised-number` - Default: `6` - `label` — The velocity line's label. - Type: `inline` - Default: `$v = u + at$` - `area-label` — The shaded displacement area's label. - Type: `inline` - Default: `$s$` - `colour` — The velocity line's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `blue` - `area-colour` — The shaded displacement area's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `teal` - `show-value` — Print the displacement on the shading, beside its label. Off by default: the number is always available to prose through the call's id, and a figure that states it as well is a choice about that figure. - Type: `bool` - Default: `false` ### physics.wave A travelling wave, with its amplitude ruled across and its wavelength braced along the axis. `phase` moves the wave along without changing anything else, so a step can show the same wave a quarter period later. - `amplitude` — The wave's amplitude. - Type: `parametised-number` - Default: `2` - `wavelength` — The wave's wavelength. - Type: `parametised-number` - Default: `4` - `phase` — How far the wave is shifted along the axis, without changing anything else. - Type: `parametised-number` - Default: `0` - `from` — Where the wave is drawn from, along the axis. - Type: `parametised-number` - Default: `0` - `to` — Where the wave is drawn to, along the axis. - Type: `parametised-number` - Default: `12` - `label` — The wave curve's label. - Type: `inline` - Default: `` - `amplitude-label` — The ruled amplitude line's label. - Type: `inline` - Default: `$A$` - `wavelength-label` — The braced wavelength's label. - Type: `inline` - Default: `$\lambda$` - `colour` — The wave curve's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `blue` - `marks-colour` — The amplitude rule and the wavelength brace's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `grey` ### physics.thin-lens A thin converging lens with the three construction rays and the image they meet at. The image is where the lens equation puts it: $v = uf/(u - f)$, with the height scaled by $-v/u$ — so the rays meet AT the image rather than near it, which is the one thing a hand-drawn ray diagram cannot promise. Distances are positive: the object stands `u` to the left of the lens, the focal points at $\pm f$ either side. The real-image case, so `u` must be greater than `f`. Nearer than the focal point the rays diverge and the image is virtual — a different construction, with the rays extended back rather than crossing. - `u` — The object's distance to the left of the lens. - Type: `parametised-number` - Default: `9` - `f` — The lens's focal length, the same either side. - Type: `parametised-number` - Default: `3` - `h` — The object's height. - Type: `parametised-number` - Default: `2` - `aperture` — The lens symbol's half-height, drawn to scale with the figure rather than the optics. - Type: `parametised-number` - Default: `2.6` - `filled` — Shade the lens symbol. - Type: `bool` - Default: `false` - `object-label` — The object arrow's label. - Type: `inline` - Default: `$O$` - `image-label` — The image arrow's label. - Type: `inline` - Default: `$I$` - `focus-label` — Both focal points' label. - Type: `inline` - Default: `$F$` - `colour` — The axis and the focal points' colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `grey` - `lens-colour` — The lens symbol's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `blue` - `ray-colour` — The three construction rays' colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `orange` - `object-colour` — The object arrow's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `teal` - `image-colour` — The image arrow's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `purple` ### physics.refraction Refraction at a boundary: the incident ray, the normal, and the refracted ray at the angle Snell's law puts it, with both angles marked. The refracted angle is $\arcsin\left(\frac{n_1}{n_2}\sin\theta_1\right)$, worked out here rather than by the author — so the ray bends towards the normal when it should, and by as much as it should. Both angles are measured FROM THE NORMAL, as they are in the subject. The boundary runs along $y = 0$ with medium 1 above it. - `angle` — The incident ray's angle from the normal, in degrees. - Type: `parametised-number` - Default: `40` - `n1` — The refractive index of the medium the incident ray travels through. - Type: `parametised-number` - Default: `1` - `n2` — The refractive index of the medium the refracted ray travels through. - Type: `parametised-number` - Default: `1.5` - `length` — Both rays' length. - Type: `parametised-number` - Default: `4` - `span` — How far the boundary and the normal are drawn either side of the point of incidence. - Type: `parametised-number` - Default: `5` - `incident-label` — The incident ray's label. - Type: `inline` - Default: `` - `refracted-label` — The refracted ray's label. - Type: `inline` - Default: `` - `normal-label` — The normal's label. - Type: `inline` - Default: `` - `angle-label` — The incident angle's label. - Type: `inline` - Default: `$\theta_1$` - `refracted-angle-label` — The refracted angle's label. - Type: `inline` - Default: `$\theta_2$` - `colour` — The normal's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `grey` - `boundary-colour` — The boundary's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `neutral` - `ray-colour` — The incident ray and its angle mark's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `orange` - `refracted-colour` — The refracted ray and its angle mark's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `purple` ## statistics Imported with `@use:statistics`. ### statistics.normal The normal density $N(\mu, \sigma^2)$, drawn across four standard deviations either side of the mean. - `mu` — The distribution's mean $\mu$. - Type: `parametised-number` - Default: `0` - `sigma` — The distribution's standard deviation $\sigma$. - Type: `parametised-number` - Default: `1` - `colour` — The curve's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `blue` ### statistics.standard-normal The standard normal $N(0, 1)$ — the density every other normal is a shift and a stretch of. It is `normal` with its parameters fixed, called the way you would call it yourself: inside the pack its own constructs are named without the pack prefix. - `colour` — The curve's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `blue` ### statistics.normal-area A normal density with the probability between two points shaded, and the bounds marked on the axis. The curve it shades is named inside the template, so two uses in one graph never collide (D36) and the caller supplies no id. - `mu` — The distribution's mean $\mu$. - Type: `parametised-number` - Default: `0` - `sigma` — The distribution's standard deviation $\sigma$. - Type: `parametised-number` - Default: `1` - `from` — The shaded region's lower bound. - Required - Type: `parametised-number` - `to` — The shaded region's upper bound. - Required - Type: `parametised-number` - `show-value` — The probability the shaded region holds — the area under the density between the bounds, read from prose as `#id.probability`. It comes from the integral rather than from a formula, so it is right for any bounds and follows a slider that moves them. Print the area on the shading itself, as a textbook diagram does. Off by default: the number is always available to prose through the call's id, and a figure that states it as well is a choice about that figure. - Type: `bool` - Default: `false` - `colour` — The curve, the shaded region and the two bound labels' colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `blue` ### statistics.mean-marker A vertical line at the mean, labelled $\mu$. - `mu` — The mean the line is drawn at. - Type: `parametised-number` - Default: `0` - `colour` — The line's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `teal` ### statistics.deviation-markers Vertical lines one standard deviation either side of the mean. - `mu` — The distribution's mean. - Type: `parametised-number` - Default: `0` - `sigma` — The distribution's standard deviation, one either side of the mean. - Type: `parametised-number` - Default: `1` - `colour` — Both lines' colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `grey` ### statistics.z-point A point sitting on a normal curve, labelled with its z-score. `x` and `z` are both supplied because a template cannot compute one from the other: substitution splices text and the value folds parse literals rather than evaluating arithmetic. Writing $z$ out is the honest form until the language grows expression abstraction. - `x` — The point's position along the axis. - Required - Type: `parametised-number` - `z` — The point's z-score, written out since a template cannot compute it from `x` and `mu`. - Required - Type: `parametised-number` - `mu` — The curve's mean, for the height the point sits at. - Type: `parametised-number` - Default: `0` - `sigma` — The curve's standard deviation, for the height the point sits at. - Type: `parametised-number` - Default: `1` - `colour` — The point's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `orange` ### statistics.uniform The continuous uniform density on $[a, b]$. - `a` — The support's lower bound. - Type: `parametised-number` - Default: `0` - `b` — The support's upper bound. - Type: `parametised-number` - Default: `1` - `colour` — The curve's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `teal` ### statistics.exponential The exponential density with rate $\lambda$. - `lambda` — The distribution's rate $\lambda$. - Type: `parametised-number` - Default: `1` - `to` — Where the curve stops being drawn. - Type: `parametised-number` - Also written: `domain-max` - Default: `6` - `colour` — The curve's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `orange` ### statistics.laplace The Laplace density with location $\mu$ and scale $b$. - `mu` — The distribution's location $\mu$. - Type: `parametised-number` - Default: `0` - `b` — The distribution's scale $b$. - Type: `parametised-number` - Default: `1` - `colour` — The curve's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `purple` ### statistics.logistic The logistic density with location $\mu$ and scale $s$. - `mu` — The distribution's location $\mu$. - Type: `parametised-number` - Default: `0` - `s` — The distribution's scale $s$. - Type: `parametised-number` - Default: `1` - `colour` — The curve's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `green` ### statistics.cauchy The Cauchy density with location $x_0$ and scale $\gamma$ — heavy enough in the tails that it has no mean, which is why it is drawn ten scales wide. - `centre` — The distribution's location $x_0$. - Type: `parametised-number` - Also written: `x0` - Default: `0` - `gamma` — The distribution's scale $\gamma$. - Type: `parametised-number` - Default: `1` - `colour` — The curve's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `red` ### statistics.t Student's $t$ with $\nu$ degrees of freedom — the normal's heavier-tailed cousin, which is what a small sample's mean actually follows. - `nu` — The degrees of freedom $\nu$. - Type: `parametised-number` - Also written: `df` - Default: `5` - `to` — How far either side of zero the curve is drawn. - Type: `parametised-number` - Default: `4` - `colour` — The curve's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `purple` ### statistics.chi-squared The chi-squared density with $k$ degrees of freedom. Drawn from just above zero: at $x = 0$ the density is infinite for $k = 1$ and the curve would have nowhere to start. - `k` — The degrees of freedom $k$. - Type: `parametised-number` - Also written: `df` - Default: `3` - `from` — Where the curve starts being drawn, just above zero. - Type: `parametised-number` - Default: `0.05` - `to` — Where the curve stops being drawn. - Type: `parametised-number` - Default: `15` - `colour` — The curve's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `green` ### statistics.f-distribution The $F$ density with $d_1$ and $d_2$ degrees of freedom — the ratio of two scaled chi-squareds, and so what a variance ratio follows. - `d1` — The numerator's degrees of freedom $d_1$. - Type: `parametised-number` - Default: `5` - `d2` — The denominator's degrees of freedom $d_2$. - Type: `parametised-number` - Default: `10` - `from` — Where the curve starts being drawn, just above zero. - Type: `parametised-number` - Default: `0.05` - `to` — Where the curve stops being drawn. - Type: `parametised-number` - Default: `5` - `colour` — The curve's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `orange` ### statistics.normal-cdf The normal's cumulative distribution — the probability of landing at or below $x$, which is the shaded area of `normal` read off as a height. - `mu` — The underlying normal's mean $\mu$. - Type: `parametised-number` - Default: `0` - `sigma` — The underlying normal's standard deviation $\sigma$. - Type: `parametised-number` - Default: `1` - `colour` — The curve's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `blue` ### statistics.exponential-cdf The exponential's cumulative distribution, $1 - e^{-\lambda x}$. - `lambda` — The underlying exponential's rate $\lambda$. - Type: `parametised-number` - Default: `1` - `to` — Where the curve stops being drawn. - Type: `parametised-number` - Default: `6` - `colour` — The curve's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `orange` ### statistics.uniform-cdf The uniform's cumulative distribution — the straight line every other CDF is measured against. - `a` — The underlying uniform's lower bound. - Type: `parametised-number` - Default: `0` - `b` — The underlying uniform's upper bound. - Type: `parametised-number` - Default: `1` - `colour` — The curve's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `teal` ### statistics.logistic-cdf The logistic's cumulative distribution — the sigmoid, which is this and nothing more. - `mu` — The underlying logistic's location $\mu$. - Type: `parametised-number` - Default: `0` - `s` — The underlying logistic's scale $s$. - Type: `parametised-number` - Default: `1` - `colour` — The curve's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `green` ### statistics.cauchy-cdf The Cauchy's cumulative distribution, in terms of $\arctan$. - `centre` — The underlying Cauchy's location $x_0$. - Type: `parametised-number` - Also written: `x0` - Default: `0` - `gamma` — The underlying Cauchy's scale $\gamma$. - Type: `parametised-number` - Default: `1` - `colour` — The curve's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `red` ### statistics.normal-tail-above The upper tail of a normal beyond a critical value — the rejection region of a one-sided test, shaded and marked. A separate construct from `normal-tail-below` rather than one with a side to choose: a template compares a chosen side as WRITTEN text, so `Upper` would quietly take the other branch. Two names cannot be got wrong. - `at` — The critical value the tail is shaded above. - Required - Type: `parametised-number` - `mu` — The distribution's mean $\mu$. - Type: `parametised-number` - Default: `0` - `sigma` — The distribution's standard deviation $\sigma$. - Type: `parametised-number` - Default: `1` - `label` — The critical value's axis label. - Type: `inline` - Default: `$c$` - `colour` — The curve, the shaded tail and the axis label's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `red` - `show-value` — Print the area on the shading itself, as a textbook diagram does. Off by default: the number is always available to prose through the call's id, and a figure that states it as well is a choice about that figure. - Type: `bool` - Default: `false` ### statistics.normal-tail-below The lower tail of a normal below a critical value. - `at` — The critical value the tail is shaded below. - Required - Type: `parametised-number` - `mu` — The distribution's mean $\mu$. - Type: `parametised-number` - Default: `0` - `sigma` — The distribution's standard deviation $\sigma$. - Type: `parametised-number` - Default: `1` - `label` — The critical value's axis label. - Type: `inline` - Default: `$c$` - `colour` — The curve, the shaded tail and the axis label's colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `red` - `show-value` — Print the area on the shading itself, as a textbook diagram does. Off by default: the number is always available to prose through the call's id, and a figure that states it as well is a choice about that figure. - Type: `bool` - Default: `false` ### statistics.bernoulli The Bernoulli mass function — one trial, drawn as the two outcomes it has. Like every discrete construct here it is a `lollipop` rather than bars: a bar chart stands on a categorical axis, and the support of a distribution is a run of numbers, so the mass sits on a numeric axis like everything else on the plane. A stem claims nothing but the value it stands at, which is what $P(X = k)$ says. - `p` — The probability of success. - Type: `parametised-number` - Default: `0.5` - `w` — The stems' line width. - Type: `number` - Also written: `width` - Default: `2` - `size` — The stems' dot size. - Type: `number` - Default: `6` - `colour` — The stems' colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `blue` ### statistics.binomial The binomial mass function $\binom{n}{k} p^k (1-p)^{n-k}$ — one stem for each $k$ from $0$ to $n$, generated from the parameters rather than written out. One `lollipop` per $k$ rather than one holding every $k$: a series is text the compiler reads in one piece, and `@for` generates ELEMENTS, so there is no way to fold a loop into an attribute. Each mass is written once either way, which is the part that matters. - `n` — The number of trials. - Type: `number` - Default: `10` - `p` — The probability of success on each trial. - Type: `parametised-number` - Default: `0.5` - `w` — The stems' line width. - Type: `number` - Also written: `width` - Default: `2` - `size` — The stems' dot size. - Type: `number` - Default: `6` - `colour` — The stems' colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `blue` ### statistics.poisson The Poisson mass function $e^{-\lambda} \lambda^k / k!$. The support is infinite, so `to` says where to stop drawing — far enough out that the tail is invisible, not so far that the stems vanish. - `lambda` — The distribution's rate $\lambda$. - Type: `parametised-number` - Default: `3` - `to` — The highest $k$ a stem is drawn for, far enough out that the tail is invisible. - Type: `number` - Default: `12` - `w` — The stems' line width. - Type: `number` - Also written: `width` - Default: `2` - `size` — The stems' dot size. - Type: `number` - Default: `6` - `colour` — The stems' colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `teal` ### statistics.geometric The geometric mass function $p(1-p)^{k-1}$ — the trial on which the first success arrives, counted from $1$. - `p` — The probability of success on each trial. - Type: `parametised-number` - Default: `0.35` - `to` — The highest $k$ a stem is drawn for. - Type: `number` - Default: `12` - `w` — The stems' line width. - Type: `number` - Also written: `width` - Default: `2` - `size` — The stems' dot size. - Type: `number` - Default: `6` - `colour` — The stems' colour. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `purple` ### statistics.effect-strip A strip of effects by group: one dot per comparison, a row per outcome, each row swarmed so a dense row reads as dense, and every dot coloured by which side of the baseline it fell on. The baseline is ruled across the plot and each side is named in a band behind the dots. The picture of a set of paired studies — organic against conventional, treatment against control — where what matters is how many land on each side and how far. `values` and `groups` are two columns of one long-format dataset: a row per comparison, its effect in one column and what it measured in the other. The groups go up the y axis as names, so the graph needs no `y format:` line. - `values` — The effect of each comparison, one per row — a dataset column bound whole. - Required - Type: `series` - `groups` — What each comparison measured, one name per row, in the same order as `values`. - Required - Type: `series` - `baseline` — No difference: where the plot is ruled and where the colours change sides. - Type: `parametised-number` - Default: `0` - `from` — The low end of the band behind the readings below the baseline. - Required - Type: `parametised-number` - `to` — The high end of the band behind the readings at or above the baseline. - Required - Type: `parametised-number` - `width` — The share of each row's room the swarm and its box take — one width for both, so a column of tied readings shrinks to stay inside the box. - Type: `number` - Default: `0.6` - `below-label` — What a reading below the baseline means, written in its band. - Type: `inline` - Default: `` - `above-label` — What a reading at or above the baseline means, written in its band. - Type: `inline` - Default: `` - `baseline-label` — The baseline's own name, written on the axis where it is ruled. - Type: `inline` - Default: `` - `below` — The dots and the band below the baseline. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `purple` - `above` — The dots and the band at or above it. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `pink` - `box-colour` — The boxes' colour, when `boxes` draws them. - Options: `neutral`, `red`, `orange`, `yellow`, `green`, `teal`, `blue`, `purple`, `pink`, `grey`, `white`, `black` - Default: `grey` - `boxes` — Draw each row's box over its swarm. The box shares the swarm's place, so it lands on the dots it summarises and leaves its outliers to them. - Type: `bool` - Default: `false`