Plotting Guide
A program that wants to give a person a plot starts at
For Agents: the fastest way is to send a plot document with
POST https://graph-paper.io/plot-link, or with
GET https://graph-paper.io/plot-link?doc=<percent-encoded JSON> from a tool
that can only fetch an address.
Tips and Tricks covers the everyday shortcuts. This page
goes deeper: how Graph Paper decides what a row means, the list and point
notation that complex plots are built from, and — if you are arriving from
Desmos — which habits carry over and which ones need a small edit.
Contents
- How Graph Paper Reads a Row
- Work with Lists and Ranges
- Index, Slice, and Filter
- Build Points and Polygons
- Draw From a Table of Data
- Restrict What Gets Drawn
- Write Piecewise Formulas
- Set Colors
- Summarize Data
- Coming from Desmos
- When a Row Draws Nothing
How Graph Paper Reads a Row
Every row in a document is read as exactly one of three things. Knowing which
one you wrote explains most surprises.
| The row is | When it looks like | What you see |
|---|---|---|
| A definition | Nothing is drawn; the name is available to every other row | |
| Data | The values are listed; nothing is drawn | |
| A plot | A series on the graph |
Two more kinds of row change values rather than draw them: an action (a \to
a + 1 ) changes variables when it runs, and a fit (Y \sim aX + b ) finds
parameter values from data. Both are covered in
Tips and Tricks.
A definition is not drawn even though it contains a formula — that is the point
of naming it. To see it, add a second row that uses the name:
Both = and \coloneq define a function. Use \coloneq when you want to be
explicit that the row is a definition rather than an equation to solve; the two
behave the same. Names can be a single letter, a letter with a subscript
(f_1 ), or a whole word.
A list on its own is data, not a plot. L = [1, 2, 3] shows you the values
and draws nothing — pair it with something that says where to draw, such as (L,
L^2) .
Work with Lists and Ranges
Lists are the backbone of complex plots. Write one directly, or generate it:
| You want | Type | You get |
|---|---|---|
| A literal list | ||
| A range | the integers 1 to 10 | |
| A range with a step | ||
| A transformed list | ||
| A comprehension | ||
| Sorted | the values in order | |
| Duplicates removed | each value once |
Arithmetic applies to every element — [1...5]^2 and the comprehension above
are two spellings of the same thing. The power form is shorter; the
comprehension is clearer once the expression grows or uses the index more than
once.
Lists combine with parameters to draw a whole family at once. Give k a set of
values and plot \sin(kx) — you get one curve per value:
Index, Slice, and Filter
Indexing starts at 1, and every form below returns a list when you give it a
list of positions.
| You want | Type | Notes |
|---|---|---|
| One element | The second element. | |
| Count from the end | The last element. | |
| A slice | Elements 2 through 4, as a list. | |
| Only some elements | Keeps the elements where the test is true. | |
| The same, spelled out | Useful when the test is not about the element itself. | |
| Transform each | The long form of |
Asking for an element that is not there gives you an undefined value rather than
an error, and points built from it are skipped when the curve is drawn. That is
deliberate — one bad index does not blank the whole plot.
Build Points and Polygons
A parenthesized pair is a point, and a point whose coordinates are lists is
a list of points. This is how scatter plots and polygons get built:
| You want | Type |
|---|---|
| One point | |
| A list of points | |
| The distance between two points | |
| The midpoint |
Points also support ordinary arithmetic, so the midpoint can be written out as
\frac{(0,0) + (3,4)}{2} if you prefer: adding two points adds their
coordinates, and dividing scales them.
Read Coordinates Back
Attach .x , .y , or .z to any point to pull out one coordinate:
| You have | Type | You get |
|---|---|---|
| A point straight out |
The accessor works on whatever produced the point, so it chains with indexing —
S[2].x is the x of the second point in a list S .
On a list of points it gives you the whole column. If S = [(0,1), (2,3),
(4,5)] , then S.x is [0, 2, 4] and S.y is [1, 3, 5] — no loop or
comprehension needed. That makes it the natural way to take one axis of a
dataset apart and put it back together differently:
plots the same points with the axes swapped. Coordinates read this way are
ordinary numbers, so they combine freely with everything else — A.x + A.y is
7 .
For a closed shape, collect the vertices into a list and pass it to
\operatorname{polygon} :
If a vertex in the list is undefined, the polygon breaks there rather than
bridging the gap — so a single undefined vertex splits one polygon into two
pieces instead of drawing a wrong edge across the middle.
Draw From a Table of Data
Once a drawing has more than a few pieces, one row per piece stops scaling — and
the repetition buries the structure of the thing you are drawing. Name the data
instead, and let a single row draw all of it.
Two rows of data: where the points are, and which points make up each piece.
That works because indexing with a list of positions returns a list: for f
= [1,2,3] , V[f] is those three points, which is exactly what
\operatorname{polygon} takes. The comprehension runs it once per entry of F .
In 3D the same split applies, naming each corner separately:
A list of vertices plus a table of faces is how shapes are described almost
everywhere, so one written this way can be read as data rather than decoded from
a wall of coordinates. Move a corner and every piece using it follows; change
which vertices a face joins and only F changes.
Restrict What Gets Drawn
There are two ways to limit a plot, and they answer different questions.
Braces restrict where the formula is true. Put a condition in braces after
any row and it applies to the whole row:
Chain them for several conditions, and use a two-sided form when it reads
better:
Braces work on every kind of row — explicit functions, implicit curves, regions,
and parametric curves:
A where clause sets a parameter's range. For parametric and polar curves,
this is the one that controls how much of the path is traced:
The same clause introduces local helpers, which keeps a long formula readable
without creating sliders:
Reach for braces when you are masking a region of the plane, and for where
when you are bounding the parameter that draws the curve.
Write Piecewise Formulas
Two spellings work, and they mean the same thing. The cases environment is the
conventional mathematical notation:
The compact brace form puts the condition and value on one line, separated by a
colon, with a final unconditional fallback:
Use whichever you find easier to read. When branches repeat an expression,
factor it into a named helper above so the branches stay short.
Set Colors
Color channels are on a 0 to 1 scale:
| You want | Type |
|---|---|
| Red | |
| Orange | |
| Mid grey | |
| By hue |
If you have channel values on the usual 0-to-255 scale, use
\operatorname{rgb}_{255} instead of converting by hand:
Assign a color to a name that way and reuse it across rows. Note that
\operatorname{rgb} itself clips values above 1, so passing 0-to-255 numbers to
the wrong one of the two fails quietly — see
Coming from Desmos.
Summarize Data
These read a list and return a single number:
| You want | Type |
|---|---|
| The total | |
| The mean | |
| The median | |
| The smallest, largest | |
| The variance | |
| The standard deviation | |
| How many elements |
For distributions, \operatorname{histogram}(L) bins the values with a default
bin width of 1, and \operatorname{histogram}(L, 2) sets the width explicitly.
Box plots are built from a table's data rather than from a formula — choose them
from the table's plot type control.
A row that reduces a list to one number plots as a horizontal line at that
value, which is a quick way to draw a mean or a threshold across a scatter plot.
Coming from Desmos
Most of what you know transfers unchanged. These all work exactly as they do
in Desmos — restriction braces, [1...10] ranges, list comprehensions,
L[L>2] filtering, .x accessors, point arithmetic, \operatorname{polygon} ,
\operatorname{histogram} , defining functions with = , single-letter names
with subscripts, and the list and statistics names \operatorname{total} ,
\operatorname{sort} , \operatorname{unique} , \operatorname{stdev} ,
\operatorname{mean} , \operatorname{median} , \operatorname{nPr} and
\operatorname{nCr} . So do the interactive features: actions written with
\to , the ticker, click actions with \mathrm{index} , draggable points,
regressions written with \sim , and ${a} templates in labels — see
Tips and Tricks for how each
one is set up here.
A few things differ:
| In Desmos | In Graph Paper | Why |
|---|---|---|
| where is the general form. | ||
| List operations are function calls. | ||
| a table's plot type | Box plots come from table data. | |
| the ticker panel | ▶ on an action cell | The interval is a row of its own. |
| clickableInfo on a row | On click… in the cell's ⋯ menu | Same \to syntax, same index. |
Desmos's list properties — L.\mathrm{total} , L.\mathrm{length} ,
L.\mathrm{min} , L.\mathrm{sort} and the rest — are all written as function
calls here. Only points take a dot, and only for .x , .y , and .z .
The color one is worth a second look, because it fails quietly. Channel
values above 1 are clipped, so a fully saturated color still looks correct and
everything else drifts toward white:
| You type | You get | |
|---|---|---|
| red | correct — by coincidence | |
| yellow | you wanted orange | |
| white | you wanted steel blue | |
| white | you wanted near-black |
Reach for \operatorname{rgb}_{255} whenever your numbers are on the 0-to-255
scale, and the conversion is done for you.
One more difference worth knowing: \operatorname{gcd} and \operatorname{lcm}
in Graph Paper work on the numbers you give them, while Desmos first rounds its
arguments to whole numbers. If you are relying on that rounding, write it out —
\operatorname{gcd}(\operatorname{round}(a), \operatorname{round}(b)) .
Importing takes care of all of this. Open a Desmos graph through import and
these conversions are applied for you; the table above is for formulas you type
yourself.
When a Row Draws Nothing
Work down this list before rewriting the formula.
| Symptom | Likely cause |
|---|---|
| The row shows values instead of a plot | It is a list or a definition, not a plot. Add a row that uses it. |
| A function name behaves like multiplication | Graph Paper does not know that name, so it reads it as a product. Check the spelling against the tables above. |
| A color came out white | Channels above 1 are clipped. Use |
| Part of a curve is missing | A brace condition or a where range is narrower than you meant. |
| A polygon has a gap | One of its vertices is undefined, which splits it in two. |
| Nothing at all appears | The viewport may be somewhere else — check the axis ranges before the formula. |
A handful of names are reserved for built-in functions, so a row like
\operatorname{mean}(x) = x^2 does not define your own function — rename it if
you need one of those.
For everything else, see Tips and Tricks or
Support.