# equation.io > Free in-browser graphing calculator (WebGL). Plots 2D curves, inequality > regions, scalar fields, vector fields and ODEs, complex functions (including > domain coloring, conformal maps and escape-time fractals), 3D surfaces, > parametric curves/surfaces, plane geometry (named draggable points, > segments, polygons, squares, circles), symbolic derivatives and integrals, > and random variables with shaded probabilities. No account, no server round-trips: the graph state > lives in the URL fragment, so you can construct a link that opens the app > with any set of equations already rendered. ## Deep links Format: https://equation.io/g/;;... - Percent-encode each equation (JavaScript `encodeURIComponent`), then additionally escape ( ) ! ' * as %28 %29 %21 %27 %2A — chat apps cut auto-linked URLs at bare parentheses, silently truncating the graph. - Encoded equations are joined with `;`. Never put a literal `;` inside an equation; it is only the row separator. - Every equation gets its own row and color. Definitions (constants, functions, coordinate fields) are rows too — order does not matter. - `/g/` links unfurl with a rendered preview image (og:image) in chat apps and social cards; the image itself is served at `https://equation.io/api/og/`. An equivalent fragment form, `https://equation.io/#`, also opens the graph and keeps the equations out of server logs, but gets no preview card. Examples (paste-ready): - [Parabola](https://equation.io/#y%3Dx%5E2): `y = x^2` - [Circle](https://equation.io/#x%5E2%2By%5E2%3D4): `x^2 + y^2 = 4` - [Sine + tangent line](https://equation.io/#f(x)%3Dx%5E3-2x;g(x)%3Dd%2Fdx%20f(x);a%3D1;y%3Df(x);y%3Df(a)%2Bg(a)(x-a)): `f(x) = x^3 - 2x; g(x) = d/dx f(x); a = 1; y = f(x); y = f(a) + g(a)(x-a)` - [Slider-controlled wave](https://equation.io/#a%3D2;y%3Dsin(a%20x)%2Fa): `a = 2; y = sin(a x)/a` - [Fourier series](https://equation.io/#N%20%3D%208;y%20%3D%202%20sum%5Bn%3D1..N%5D%20(-1)%5E(n%2B1)%20sin(n%20x)%2Fn): `N = 8; y = 2 sum[n=1..N] (-1)^(n+1) sin(n x)/n` — drag N for more harmonics - [Animated ripples](https://equation.io/#sin(x%5E2%2By%5E2-4t)%2F2): `sin(x^2+y^2-4t)/2` - [3D saddle](https://equation.io/#z%3D(x%5E2-y%5E2)%2F4): `z = (x^2 - y^2)/4` - [Torus (parametric 3D)](https://equation.io/#(cos(2pi%20u)(2%2Bcos(2pi%20v))%2C%20sin(2pi%20u)(2%2Bcos(2pi%20v))%2C%20sin(2pi%20v))): `(cos(2pi u)(2+cos(2pi v)), sin(2pi u)(2+cos(2pi v)), sin(2pi v))` - [Complex dipole field](https://equation.io/#ln(w-2)-ln(w%2B2)): `ln(w-2) - ln(w+2)` - [Domain coloring](https://equation.io/#domain((w%5E3%20-%201)%2Fw)): `domain((w^3 - 1)/w)` - [Conformal grid image](https://equation.io/#conformal(w%5E2%2F4)): `conformal(w^2/4)` - [Mandelbrot set](https://equation.io/#iter(z%5E2%20%2B%20w)): `iter(z^2 + w)` - [Julia set](https://equation.io/#iter(z%5E2%20-%200.7269%20%2B%200.1889i)): `iter(z^2 - 0.7269 + 0.1889i)` - [Vector field](https://equation.io/#(-y%2C%20x)): `(-y, x)` — drawn as animated streamlines - [Pendulum phase portrait](https://equation.io/#(x'%2C%20y')%20%3D%20(y%2C%20-sin(x))): `(x', y') = (y, -sin(x))` - [Slope field](https://equation.io/#y'%20%3D%20x%20-%20y): `y' = x - y` - [Polar cardioid](https://equation.io/#r%3Dsqrt(x%5E2%2By%5E2);theta%3Datan2(y%2Cx);r%3D2(1%2Bcos(theta))): `r = sqrt(x^2+y^2); theta = atan2(y,x); r = 2(1 + cos(theta))` - [Circle through a draggable point](https://equation.io/#C%20%3D%20(0%2C%200);P%20%3D%20(2%2C%201);circle(C%2C%20%7CP%20-%20C%7C);segment(C%2C%20P)): `C = (0, 0); P = (2, 1); circle(C, |P - C|); segment(C, P)` - [Thébault's theorem (squares on a parallelogram)](https://equation.io/#A%20%3D%20(0%2C%200);B%20%3D%20(4%2C%200.5);D%20%3D%20(1%2C%202.5);C%20%3D%20B%20%2B%20D%20-%20A;polygon(A%2C%20B%2C%20C%2C%20D);square(B%2C%20A);square(C%2C%20B);square(D%2C%20C);square(A%2C%20D);P%20%3D%20midpoint(A%2C%20B)%20-%20perp(B%20-%20A)%2F2;Q%20%3D%20midpoint(B%2C%20C)%20-%20perp(C%20-%20B)%2F2;R%20%3D%20midpoint(C%2C%20D)%20-%20perp(D%20-%20C)%2F2;S%20%3D%20midpoint(D%2C%20A)%20-%20perp(A%20-%20D)%2F2;polygon(P%2C%20Q%2C%20R%2C%20S)): `A = (0, 0); B = (4, 0.5); D = (1, 2.5); C = B + D - A; polygon(A, B, C, D); square(B, A); square(C, B); square(D, C); square(A, D); P = midpoint(A, B) - perp(B - A)/2; …; polygon(P, Q, R, S)` — drag A, B or D; the square centers stay a square - [Normal probability](https://equation.io/#m%20%3D%201;s%20%3D%200.5;X%20~%20Normal(m%2C%20s);P(0%20%3C%20X%20%3C%202)): `m = 1; s = 0.5; X ~ Normal(m, s); P(0 < X < 2)` — the density with the area between 0 and 2 shaded; drag m or s and the probability follows - [Swinging pendulum (integrated state)](https://equation.io/#th'%20%3D%20om;om'%20%3D%20-sin(th)%20-%20om%2F8;th(0)%20%3D%203;bob%20%3D%20(sin(th)%2C%20-cos(th));segment((0%2C%200)%2C%20bob);bob): `th' = om; om' = -sin(th) - om/8; th(0) = 3; bob = (sin(th), -cos(th)); segment((0, 0), bob); bob` — th is the angle, om the angular velocity; segment() draws the rod, the named point draws the bob - [Double pendulum](https://equation.io/#g%20%3D%209.8;L1%20%3D%201;L2%20%3D%201;m1%20%3D%201;m2%20%3D%201;M%20%3D%20%5B((m1%2Bm2)%20L1%2C%20m2%20L2%20cos(th_1%20-%20th_2))%2C%20(L1%20cos(th_1%20-%20th_2)%2C%20L2)%5D;f%20%3D%20(-m2%20L2%20om_2%5E2%20sin(th_1%20-%20th_2)%20-%20(m1%2Bm2)%20g%20sin(th_1)%2C%20L1%20om_1%5E2%20sin(th_1%20-%20th_2)%20-%20g%20sin(th_2));th'%20%3D%20om;om'%20%3D%20solve(M%2C%20f);th(0)%20%3D%20(2.5%2C%202.4);b1%20%3D%20(L1%20sin(th_1)%2C%20-L1%20cos(th_1));b2%20%3D%20b1%20%2B%20(L2%20sin(th_2)%2C%20-L2%20cos(th_2));segment((0%2C%200)%2C%20b1);segment(b1%2C%20b2);b1;b2): `M = [((m1+m2) L1, m2 L2 cos(th_1 - th_2)), (L1 cos(th_1 - th_2), L2)]; f = (…); th' = om; om' = solve(M, f); th(0) = (2.5, 2.4); …` — the Lagrangian form M(θ)ω′ = f: th and om are 2-vector states with components th_1, th_2; solve() is Cramer's rule; segment()s between named bob points draw the linkage - [Orbit (vector gravity)](https://equation.io/#r'%20%3D%20vel;vel'%20%3D%20-9%20r%2F%7Cr%7C%5E3;r(0)%20%3D%20(2%2C%200);vel(0)%20%3D%20(0%2C%201.5);segment((0%2C%200)%2C%20r);r;(0%2C%200)): `r' = vel; vel' = -9 r/|r|^3; r(0) = (2, 0); vel(0) = (0, 1.5); segment((0, 0), r); r; (0, 0)` — vector states with point arithmetic; the bare state name draws as a moving point - [Matrix phase portrait](https://equation.io/#a%20%3D%20-1;b%20%3D%20-1%2F4;A%20%3D%20%5B(0%2C%201)%2C%20(a%2C%20b)%5D;(x'%2C%20y')%20%3D%20A%20(x%2C%20y)): `a = -1; b = -1/4; A = [(0, 1), (a, b)]; (x', y') = A (x, y)` — a linear system as its literal matrix, sliders in the entries - [Central limit theorem](https://equation.io/#X1%20~%20Uniform%280%2C%201%29;X2%20~%20Uniform%280%2C%201%29;X3%20~%20Uniform%280%2C%201%29;X4%20~%20Uniform%280%2C%201%29;S%20%3D%20X1%20%2B%20X2%20%2B%20X3%20%2B%20X4;Z%20~%20Normal%282%2C%20sqrt%281%2F3%29%29;P%28S%20%3E%203%29): `X1 ~ Uniform(0, 1); X2 ~ Uniform(0, 1); X3 ~ Uniform(0, 1); X4 ~ Uniform(0, 1); S = X1 + X2 + X3 + X4; Z ~ Normal(2, sqrt(1/3)); P(S > 3)` — the sum of four uniforms hugs the matching normal - [Conditional random variable](https://equation.io/#X%20~%20Normal%280%2C%201%29;Y%20%3D%20%7BX%20%3E%200%3A%20X%5E2%2C%201%7D;P%28Y%20%3E%200.5%29;P%28Y%20%3E%20X%29): `X ~ Normal(0, 1); Y = {X > 0: X^2, 1}; P(Y > 0.5); P(Y > X)` — a piecewise transform of X, with probabilities of derived and joint events ## Share links with preview images (/g/) The same payload also works as a path, and this form unfurls with a rendered preview image (og:image) in chat apps and social cards: https://equation.io/g/;;... Prefer the `/g/` form when the link will be posted somewhere that shows link previews; prefer the `#` form otherwise (it keeps the equations out of server logs). Both open the same app with the same graph. The preview image itself is served at `https://equation.io/api/og/`. ## MCP server An MCP endpoint (Streamable HTTP) lives at: https://equation.io/mcp Tools: `create_graph` (validates each equation through the app's own parser, returns url/share_url plus per-row classification or errors, and attaches a rendered PNG preview of the graph; rows the preview renderer cannot draw are listed in `preview_omits` with the reason — the live app renders more than the static preview, so an omission never means the row failed) and `read_graph` (decodes an existing link back into its equation rows so you can edit a user's graph and build a new link). This document is also served as the server's `syntax` MCP resource, so the full reference can be pulled on demand instead of living in a tool description. No authentication required. ## Expression syntax - Operators: `+ - * / ^`, parentheses, unary minus. Multiplication is implicit: `2x`, `x y`, `2cos(t)`, `a(x-1)` all work. - Absolute value bars: `|x|`, including nested/multiplied forms like `2|x-1|`. - Constants: `pi`, `tau`, `e`. Imaginary unit `i` in complex expressions. - Functions: sin cos tan asin acos atan atan2(y,x) sinh cosh tanh sech asinh acosh atanh sqrt abs exp ln (natural) log (base 10) floor ceil round min max mod sign fract, complex helpers re im arg conj, and point helpers dot cross perp midpoint unit. Function names are case-insensitive: `Sin(x)`, `SQRT(x)` and `sin(x)` are the same call. Always use parentheses for arguments — write `sin(x)`, not `sin x`. - Derivatives: `d/dx (x^3)` differentiates symbolically; higher order `d^2/dx^2 (...)`; works for any single-letter variable, e.g. `d/dq (q^2)`. - Sums and products: `sum(n=1..N, sin(n x)/n)` and `prod(k=1..N, k)`, also written `Σ`/`Π`. The bracket form `sum[n=1..N] sin(n x)/n` takes everything after it in the product chain as the body (`2 sum[n=1..N] ...` keeps the 2 outside). Bounds must be numbers or constants defined in another row (not `t`), because the sum is expanded symbolically before compiling — which makes `N` a slider that adds terms as you drag it. - Integrals: `int[a..b] f(x) dx` (definite) and `int(f(x) dx)` (antiderivative), also written `∫`. The trailing `dx` names the variable; any letter works, so `y = int[0..x] exp(-t^2) dt` plots a function of x, and bounds may use sliders, t, or x. Integration is symbolic where a verified antiderivative exists — polynomials and rational functions (exactly), the elementary families, u-substitution, integration by parts, Gaussians via erf — and falls back to Gauss–Legendre quadrature expanded in place otherwise, so non-elementary rows like `y = int[0..x] sin(t)/t dt` (Si) still plot. A row whose integral resolves to a constant shows the numeric value as a readout. Classic notations work: `int[0..1] dx/(1+x^2)`, and iterated integrals pair inside-out: `int[0..1] int[0..y] x dx dy`. Bounds may be `±inf` (or `∞`): `int[-inf..x] exp(t) dt` is exp(x), `int[-inf..inf] exp(-x^2) dx` reads √π, and `int[-inf..x] normalpdf(t, 0, 1) dt` is the normal cdf. Divergent limits are refused (the closed form is checked against real quadrature), not misreported. - Comments: a row starting with `#` (e.g. `# tangent lines`) plots nothing; in the app it renders as a heading whose group (the rows until the next `#` row) can be collapsed. Use them to label sections of longer documents. ## Variables (what a row means) - `x`, `y`: the 2D plane. `y = f(x)`, or any implicit equation in x and y (`x^2+y^2=4`), renders as a curve. - `z`: using z switches to 3D. `z = sin(x)cos(y)` is a surface; `x^2+y^2+z^2=9` is an implicit 3D surface. - `t`: time in seconds. Any row mentioning `t` animates, e.g. `y = sin(x - 2t)`. - `u`, `v`: parametric parameters, each ranging over (0, 1). - `w`: the complex plane point x + iy. A complex-valued expression in `w` (e.g. `ln(w-2) - ln(w+2)`) draws field lines and equipotentials. Wrapping it in `domain(…)` or `conformal(…)` picks a different view of the same function. - `z` inside `iter(…)`: the iterate being fed back through the step. Outside `iter`, `z` is the 3D axis as above. ## Row types - Curve: an equation in x and y — `y = x^2`, `(x^2+y^2)^2 = 8(x^2-y^2)`. - Region: inequalities, including chains — `y < x/2 + 1`, `4 <= x^2 + y^2 <= 9`. Non-strict comparisons draw a solid boundary. - Scalar field: a bare expression in x, y — `sin(x)cos(y)` — shaded by value. - Complex plot: a complex expression in `w` — `w + 4/w`. - Domain coloring: `domain(f)` where f is complex — `domain(w^2 + 1)`. Hue is arg f, brightness |f|; zeros render black and poles white. - Conformal map: `conformal(f)` — `conformal(w^2/4)` — draws the image of the coordinate grid under f (the level curves of re f and im f). - Fractal: `iter(step)` iterates `z ↦ step`, coloring by escape time. Inside `iter` only, `z` is the iterate. If the step mentions the plane (`w`, `x`, `y`) iteration starts at 0, so `iter(z^2 + w)` is the Mandelbrot set; with a fixed constant the pixel is the starting point, so `iter(z^2 - 0.8 + 0.156i)` is a Julia set. An optional second argument sets the iteration count, `iter(z^2 + w, 600)` (default 250, max 5000). Any expression works: `iter((|re(z)| - i |im(z)|)^2 + w)` is the burning ship. - Point: `(2, 3)`; animate with t: `(2cos(t), 2sin(t))`. - Geometry: with points defined in rows above (`A = (0, 0)`), `segment(A, B)`, `line(A, B)` (the infinite line through A and B), `polygon(A, B, C, …)` (filled), `square(A, B)` (erected on side A→B, to the left of the direction A→B), and `circle(center, radius)` draw figures that follow their points. Point arithmetic works anywhere: `A + B`, `2A`, `(A + B)/2`, `midpoint(A, B)`, `perp(A)` (rotate 90° counterclockwise), `unit(A)`, `dot(A, B)`, `cross(A, B)`, `|A - B|` (distance). A parametric point like `A + u(B - A)` traces the segment. Tuple literals work directly as arguments (`segment((0, 0), A)`) and as operands: `A + (1, 2)`, `((0, 0) + B)/2`, `|(3, 4)|`. - Vector field: a 2-tuple in x, y — `(-y, x)` — drawn as animated streamlines (line-integral convolution); clicking the canvas traces integral curves. - ODE / slope field: `dy/dx = x y` or `y' = sin(x) - y` plots the direction field (1, f); a system `(x', y') = (y, -sin(x))` plots its phase portrait. (Primes on x/y/z mean this. A prime on a name of your own is a state — see below — so `y' = -y` is a slope field while `th' = -th` is a simulation.) - Random variable: `X ~ Normal(mean, sd)` declares X and plots its density curve. Parameters may use sliders and t, so `X ~ Normal(0, a)` follows the slider live. - Probability: `P(X < 2)`, `P(X > 0)` or `P(0 < X < 2)` shades that area under X's density and shows the numeric probability (normal CDF) as a readout under the row. - Expectation: `E(X)`, `E(X^2 + Y)` — the mean of any expression in random variables, shown as a readout under the row and marked on the density. - Parametric curve: components in u — `(2cos(2pi u), sin(4pi u))`; three components make a 3D curve — `(2cos(6pi u), 2sin(6pi u), 4u - 2)`. - Parametric surface: three components in u and v — `(cos(2pi u)(2+cos(2pi v)), sin(2pi u)(2+cos(2pi v)), sin(2pi v))`. - 3D surface: any equation in x, y, z. - Random variable: `X ~ Normal(mean, sd)` — also `Uniform(lo, hi)` and `Exponential(rate)`, with short aliases `N`, `U`, `Exp`; a bare name means the standard parameters (`X ~ N` is Normal(0, 1)). The row plots the exact density curve; parameters may use sliders and t. - Derived random variable: `Y = X^2`, `S = X1 + X2` — arithmetic over declared random variables declares a new one, including piecewise conditionals: `Y = {X > 0: X^2, 1}`. Distinct names are independent, so `S = X1 + X2` is the convolution; the same name stays dependent, so `X + X` is exactly 2X. A bare expression in random variables (`X + Y` as its own row) plots that density without naming it. Affine forms stay exact: combinations of normals (`Z = (X + Y)/2`) are the exact normal, a scaled/shifted uniform is a uniform, and sums of uniforms (`S = X1 + X2`, the central-limit demo) draw their exact piecewise-polynomial convolution — the triangle's apex is a true corner. These rows report exact μ and σ. Everything else (products, nonlinear or piecewise transforms, mixed families) is estimated from 131072 joint samples and reports μ, σ as `≈`. Point masses are detected and drawn as probability stems (height = mass): `Y = {X > 0: 1, 2}` is two stems, `floor(4X)` four, and a mixed result like `{X > 0: X^2, 1}` draws its stem plus the continuous density. - Probability: `P(X < 2)`, `P(-1 < X < 2)`, and bounds around one expression like `P(0.5 < X + Y < 1.5)` — exact (closed-form CDF or polynomial integration) with the area under the density shaded, whenever the bounded variable or expression has an exact law. Any other inequality — `P(Y > 0.5)` for a nonlinear Y, `P(Y > X)` — is estimated from the joint samples; the row shows the value as `≈`. - Expectation: `E(X)`, `E(2X + 3)`, `E(X^2 + Y)` — the mean of any expression in the declared variables, exact whenever the expression has an exact law (closed-form pdfs, affine combinations of normals, uniform sums) and the finite-sample mean otherwise. The row shows the value as a readout and draws a vertical marker at x = E under the expression's density. ## Definitions (extra rows that set up the others) - Constant: `a = 2` — the UI shows a draggable slider for it. Computed constants allowed: `b = a^2 + t`. - Function: `f(x) = x^3 - a x` — call it from other rows; calls inline symbolically, so `d/dx f(x)` works. - Point: `A = (0, 0)` — a named, labeled point drawn on the canvas; the user can drag it and every row using it follows. Points may be computed from other points defined above: `C = B + D`, `M = midpoint(A, B)`, `P = midpoint(A, B) - perp(B - A)/2`. Scalars over points work too: `n = cross(B, D)`, `L = |B - A|`. - Dragging: a point (named or bare) is draggable only where the drag has somewhere to write the new value: a coordinate that is a plain number (`(2, 3)`) or a bare slider name (`(a, 3)` with `a = 1` above) moves; computed coordinates (`C = B + D`, `(a+1, cos(t))`) are pinned, and 3D graphs have no point dragging. The MCP create_graph result marks each point row with `"draggable"` — write coordinates as literals or sliders when the user is meant to move the point. - State (simulation): `th' = -sin(th)` defines the time-derivative of `th`, integrated forward (RK4) as the graph animates, starting from `th(0) = 2` (0 if omitted). Everywhere else `th` then behaves exactly like a constant, so plot the system by drawing with it: `bob = (sin(th), -cos(th))` names the swinging bob and `segment((0, 0), bob)` draws its rod — points, segment() and polygon() all accept state-valued coordinates. Use this for anything with no closed form — coupled oscillators, a double pendulum, Lorenz. - Vector state: a state whose derivative (or starting value) is a 2- or 3-vector integrates componentwise: `r' = vel; vel' = -r/|r|^3; r(0) = (1, 0); vel(0) = (0, 1)` is an orbit. Components are `r_1`, `r_2` (and `r_3`) — use them anywhere a scalar goes — and the bare name `r` draws as a moving point and works in point arithmetic (`perp(r)`, `dot(r, r)`, `r/|r|`, segment endpoints). 3-component states (Lorenz) plot through their components. - Matrix: `M = [(a, b), (c, d)]` (also `[[a, b], [c, d]]`, or named points as rows) defines a 2×2 or 3×3 matrix. `det(M)`, `trace(M)`, the matvec `M v`, and `solve(M, v)` (Cramer's rule) expand symbolically, so entries may hold sliders, t, x/y, or states, and results work anywhere: `(x', y') = A (x, y)` is a linear phase portrait, and `th' = om; om' = solve(M, f)` integrates a system written in the Lagrangian form M(θ)ω′ = f — the natural shape for a double pendulum. Name states for what they are (`th`/`om` for angle and angular velocity, `pos`/`vel` for a spring — the plane's own x/y/z are reserved). A derivative may use t, constants and other states (not x/y); an initial value must be constant, and gets a slider that relaunches the run when dragged. A ↻ button in the panel restarts it. - Coordinate field: a definition that depends on the plane, e.g. `r = sqrt(x^2+y^2); theta = atan2(y,x)`. Its level sets draw as a grid, and later rows may use it: adding `r = 2(1 + cos(theta))` plots a cardioid in polar coordinates. ## Viewport rows (framing) - `view(x = -5..5, y = -2..2)` sets the 2D window; either axis alone works (`view(x = 0..0.01)`), the other is centered at 0 and sized by the canvas aspect. The whole box is always visible (uniform scale). - `camera(theta, phi, radius, (tx, ty, tz))` aims the 3D orbit camera — angles in radians (`camera(-pi/3, 0.6)`), radius and target optional. - Binding is two-way in the app: panning, zooming, or orbiting rewrites the row the way dragging a slider rewrites its constant, so the URL always names the exact picture on screen. Without a viewport row, interaction stays ephemeral and the default framing applies. - Bounds and angles may use `pi` and defined constants, evaluated at load. - The /g/ preview card and the MCP create_graph preview image honor these rows, so they show the framing you chose. ## Guidance for assistants - To show a user a graph, emit a single https://equation.io/g/... link built as above; the graph renders immediately on load, no interaction needed. - To edit a graph the user already has, decode their link (or call the MCP `read_graph` tool), transform the rows, and emit a new link. Prefer restructuring into named definitions: e.g. given `y = x^2 - 2x`, "add a tangent line at x = 3" becomes `f(x) = x^2 - 2x; g(x) = d/dx f(x); a = 3; y = f(x); y = f(a) + g(a)(x - a)` — with `a` as a draggable slider so the point of tangency stays explorable. - Prefer a slider constant (`a = 1; ...`) when the user may want to explore a parameter, and `t` when they ask for animation. - The graph state lives entirely in the link, and the link is the interface. The only server endpoints are /mcp (an MCP server that validates equations and emits links) and /api/og (link-preview images). - If you are unsure whether a form is supported, emit the link anyway. Unsupported input fails loudly and visibly (an inline error on the offending row), never silently — so a graph either renders correctly or tells the user what to fix. There is no wrong-but-silent output to worry about. ## More - [App](https://equation.io/): the grapher itself - [About](https://equation.io/about/): feature gallery with screenshots - [robots.txt](https://equation.io/robots.txt): crawling policy