The workhorse figure of experimental work: data as markers with error bars, a model as a smooth line. In Plotly those are just two traces with the right mode.
Step 1 — Data as markers, model as line
The mode property is the key: measured points are markers (never connected — data are points), the model is lines:
const x = [1, 2, 3, 4, 5, 6, 7, 8];
const y = [2.1, 3.9, 8.3, 15.8, 31.0, 44.2, 58.9, 71.5];
const model = { x: [], y: [] };
for (let t = 1; t <= 8; t += 0.05) {
model.x.push(t);
model.y.push(1.15 * t * t);
}
const data = [
{ x, y, mode: 'markers', type: 'scatter', name: 'measured' },
{ x: model.x, y: model.y, mode: 'lines', type: 'scatter', name: 'model' }
];
Plotly.newPlot(chart, data);
Note the model is evaluated on a fine grid (t += 0.05) so the curve is smooth — never draw the model through only the data points.
Step 2 — Add error bars
Uncertainties attach directly to the data trace:
const yerr = [0.4, 0.5, 0.9, 1.4, 2.2, 2.7, 3.1, 3.6];
const data = [
{
x, y, mode: 'markers', type: 'scatter', name: 'measured',
error_y: { type: 'data', array: yerr, visible: true, width: 3 }
},
{ x: model.x, y: model.y, mode: 'lines', type: 'scatter', name: 'model' }
];
Plotly.newPlot(chart, data);
type: 'data' means “these are my actual per-point uncertainties” — there are also 'percent' and 'constant' modes, but real error bars from real numbers is almost always what a paper needs.
Step 3 — Axis titles and ranges
Plotly.newPlot(chart, data, {
xaxis: { title: { text: 'Concentration (mM)' } },
yaxis: { title: { text: 'Rate (1/s)' }, rangemode: 'tozero' },
font: { family: 'Times New Roman', size: 14 },
legend: { x: 0.02, y: 0.98 }
});
rangemode: 'tozero' keeps the y-axis anchored at zero — starting a bar-or-rate axis at an arbitrary value is how figures accidentally exaggerate effects.
Step 4 — Export or insert
Run, then Export / Insert — a vector PDF for LaTeX or straight into the paper with a \label. The interactivity (hover values, zoom) is your tool while iterating; the exported figure is the static, typeset result.
Tip: For log-scale data, add
type: 'log'to the axis instead of transforming your numbers — the tick labels stay in real units, which is what readers need to check your values.