docs

How the Digitizer works

A reference for every concept in Plotion: the calibration model behind each chart type, how margin of error is computed, and what each export format contains.

Overview

Plotion turns an image of a chart into numeric data. You upload an image, tell Plotion what the axes mean (calibration), mark the data points, and export the result as CSV, JSON, or XLSX. Everything runs in your browser: the chart image, the calibration, and every point you place never touch our servers.

Each chart type gets its own calibration model instead of one generic fit, because charts don't all encode values the same way: an XY scatter plot reads values off two axes, a Pie chart reads values as angles from a center point, a Ternary chart reads values as barycentric coordinates inside a triangle. The sections below cover each one.

The 5-step flow

Every chart, regardless of type, goes through the same five steps in order. Each step is gated by the completeness of the one before it.

  1. Upload: add the chart image (up to 10 MB).
  2. Setup: choose the chart type, and for chart types that use them, the scale for each axis (and category labels if the axis is categorical).
  3. Calibrate: set the calibration targets for the chosen chart type. The input form adapts to the axis scale (a number, a date + granularity, or the two ends of a category list).
  4. Pick: mark data points on the image. Manage series here too (add, rename, recolor, switch line/scatter).
  5. Export: generate the file, long or wide, in CSV, JSON, or XLSX.

Changing an axis scale in Setup after you've already calibrated resets that axis's calibration (with a confirmation prompt). The pixel positions of your points don't move; their computed values are recalculated once you recalibrate.

Axis scales

XY, Bar, and Geo charts use axis scales to map a pixel position to a data value. X and Y can use different scales independently (except where a chart type locks one, see below). Four kinds exist:

  • Linear: straight-line interpolation between two axis references.
  • Log: straight-line interpolation in log space; the effective scale (units per pixel) changes along the axis, which is why margin of error widens toward one end of a log axis.
  • Date/time: internally stored as epoch milliseconds, interpolated linearly in that space. Has a granularity (year / month / day / datetime) that controls input precision and display format.
  • Category: a discrete axis. You provide a list of labels, then click the position of the first and last category (equal spacing is assumed; individual ticks can be nudged). Picking snaps to the nearest category.

Internally, every data value is a plain number: the raw number for linear/log, epoch-ms for date, and a category index for category. Text only exists at the edges (typing a value in, or formatting one for display/export) - it is never what gets stored on a point.

Series

A series is a named group of data points, for example separate lines on one chart. Each series has a name, a color, and a type: line (points are connected and sorted) or scatter (points stand alone). Type is set per series, not per chart. While picking, one series is active; new points go into it. You can move a point to a different series from the points table.

Line series are sorted before export and before drawing the connecting line: by X value for XY/Bar/Geo/Scale-bar, but by anglefor Polar (a polar curve winds around by angle, not by radius). Pie and Bar don't use freeform series the same way; see their sections below.

Chart type: XY

Scatter and line charts on two independent axes. Calibration sets four calibration targets: X1, X2 (two known points on the X axis) and Y1, Y2 (two known points on the Y axis) - shown as an Axis reference pair (pixel + value) for each. Points are freeform: click anywhere, any number of points per series.

Both axes can independently use any of the four axis scales. This is the reference chart type: Bar and Geo reuse the exact same Transform{x, y} calibration model, just with different rules layered on top.

Chart type: Bar

Grouped or stacked bars, vertical or horizontal. Calibration is the same four-target model as XY, but with constraints: the category axis is locked to the category scale, and the value axis is locked to linear or log. Orientation just swaps which axis plays which role.

A Bar data point is uniquely identified by (category, series): clicking the same category+series slot again replaces the old value instead of adding a new point, unlike XY's freeform points. In stackedmode, a segment's value is the scale value at the click position minus the sum of the series below it in the same category; stack order is set by dragging series in the Series panel (first series = closest to the baseline). 100%-stacked charts aren't a separate mode - calibrate the value axis to 0-100 instead.

Chart type: Pie / donut

Pie and donut charts read values as angles from a center point, which a single pixel-to-value axis can't represent - Pie uses its own calibration model instead of the XY four-target one. Two calibration targets: Center and Start(a point on the rim marking the 0° angle). Donut is treated exactly like pie; the hole radius isn't modeled.

Setup has one field beyond the usual: Direction (clockwise or counterclockwise, default clockwise) - the direction angles are measured from Start. Getting this wrong makes every percentage wrong, not just slightly off.

One click per active series marks where that slice ends. Slice order is inferred automatically from the sorted click angles (no manual reordering, unlike Bar's stack order). Percentages are normalized to sum to 100% (unlike Bar, where the total isn't known in advance). The raw angle sum vs. 360° is shown separately as closure error, a chart-wide precision indicator distinct from per-point margin of error. Export has no long/wide toggle - there's only one table shape, with series, percent, and degrees (the raw angle, for auditing).

Chart type: Polar

Freeform points in radius/angle form, like a scatter plot in polar coordinates. Three calibration targets: Center (pixel only), Start (marks angle 0° and gives your first radius reference - click and type its value), and R2 (a second radius reference at any angle, also typed). Radius reuses the same axis scale machinery as XY/Bar (linear or log, locked in Setup) once distance-from-center is reduced to a single number; angle is bespoke, using the same Direction setting as Pie.

Series work like XY (freeform, line or scatter, any number of points). A line series is sorted by angle, not radius, so spirals and flower-shaped curves draw correctly. Export columns: series, r, theta_deg, delta_r - long format only, no wide (Polar has no independent axis to group rows by).

Chart type: Ternary

Three-axis composition charts (A/B/C, summing to 100%) inside a triangle - common for things like sediment or mineral composition. Calibration is three targets, no typed values needed: vertex A, vertex B, vertex C, positioned by pixel only. Each vertex's role (100% on its own axis, 0% on the other two) is implicit.

A clicked point's A and B percentages come from standard barycentric coordinates relative to the three vertices; axis C is derived on export as 100 - a - b, never stored, since barycentric coordinates always sum to 100% by construction (no closure error step needed, unlike Pie). Axis labels are generic A/B/C for now. Export columns: series, a, b, c, delta- one combined margin-of-error number, because nudging a point by one pixel changes all three axes at once (they're not independent like X/Y). Long format only.

Chart type: Geo

Latitude/longitude charts. Structurally identical to XY - same four-target calibration, same freeform points - with X and Y locked to linearonly (log/date/category don't make sense for geographic coordinates), and cosmetic “Longitude”/“Latitude” labels in place of X/Y.

Values assume an equirectangular projection: longitude and latitude map linearly from pixel position, with no correction for real map projections (Mercator and friends). This is accurate for small regional or schematic maps, and increasingly inaccurate for whole-world maps with real projections - a documented limitation, not a bug. No degrees-minutes-seconds input; decimal degrees only. Long and wide export both work, same as XY.

Chart type: Scale-bar

For measuring physical positions on any image that has a reference scale drawn on it - a rock outcrop photo, a microscope slide, a site plan - like placing a ruler on the photo. Two calibration targets: Origin (the (0,0) position, pixel only) and Ref (a second point with its real-world distance from Origin typed in). The Origin→Ref segment sets one scale factor (physical units per pixel) used for both X and Y - unlike XY/Bar/Geo, which calibrate X and Y independently.

X/Y axis directions follow the image's pixel orientation as-is; they are not rotated to match the Origin→Ref segment, so a slightly rotated source photo carries that rotation into your x/y values. Points are freeform, multi-series, like XY. Distance from Origin is derived on export as hypot(x, y), not stored separately. Because the scale is isotropic, delta_x and delta_y both get the same margin-of-error magnitude (unlike Pie/Polar/Ternary, which leave one of the two error slots null). Export columns: series, x, y, distance, delta. The distance unit (cm/m/km, whatever you type) isn't validated or shown as a label - it's on you to stay consistent.

Margin of error

Every point gets a computed margin of error instead of a flat estimate, derived from two pixel-space error sources added in quadrature: quantization (~0.5px, how precisely you can click) and calibration (~2px, how precisely the calibration targets themselves were placed):

error = sqrt((0.5 × scale)² + (2 × scale)²)

where scale is the axis's local scale(data units per pixel) at that point's position. For linear axes this is constant everywhere; for log axes it changes along the axis, so the same pixel error translates to a larger data-value error at the high end of a log scale than at the low end. Category axes have no meaningful pixel-to-value error, so delta_x/delta_y is null there.

Chart types without a plain X/Y axis compute this differently: Ternary combines all three axes into one number (a pixel nudge moves A, B, and C together, so they're not independent); Pie has no per-point error at all, only chart-wide closure error.

Export formats

Three file formats: CSV, JSON, and XLSX. For chart types built on a plain X/Y axis (XY, Bar, Geo), two table shapes are available:

  • Long (default): one row per point - series, x, y, delta_x, delta_y, plus x_epoch_ms when the X axis is a date.
  • Wide: one row per distinct X value, with one Y column per series. Rows with the same X are merged.

Pie, Polar, Ternary, and Scale-bar don't have an independent axis to group wide-format rows by, so they export long format only, with columns specific to that chart type (documented in each chart type's section above). Line series are sorted before export - by X for XY/Bar/Geo/Scale-bar, by angle for Polar.

Data & privacy

The chart image you upload never leaves your browser. There is no server-side project storage or image upload endpoint at all. Your working session is split across two browser stores: calibration, points, and series configuration in localStorage; the chart image itself in IndexedDB (images can be up to 10 MB, larger than what localStorage alone can hold). Both auto-save as you work.

Clear session, available in the Digitizer, wipes both stores in one click. Because everything lives on your device, a working session is tied to one browser on one machine - there's currently no way to resume a chart on a different device.

Accounts & invite codes

Registration currently requires a one-time invite code created by an admin. A code has a label, expires (14 days by default unless the admin sets otherwise), and is single-use: unused, used, revoked, or expired. Sign-in creates a signed session cookie valid for 7 days.

See our Privacy Policy and Terms of Service for the full account and data-handling terms.