RunMat
GitHub
Back to Blog

MATLAB Plotting in RunMat: Complete Guide to 2D, 3D & GPU-Accelerated Figures

Published04/03/2026
20 min read

MATLAB Plotting in RunMat

TL;DR: RunMat supports 20+ plot types (plot, scatter, surf, contour, bar, hist, subplot, and more) with MATLAB-compatible syntax, all GPU-rendered in the browser via WebGPU. Every code example on this page is runnable in the sandbox with no install. Scroll to 2D plots, 3D surfaces, animation, or the FAQ.

The plot is how you verify your math

RunMat renders MATLAB-compatible plots in the browser using WebGPU. You write plot(x, y), surf(X, Y, Z), or any of the 20+ supported plot types with the same syntax you already know, and the GPU renders the result. No install, no server, no Java figure system. When computation also runs on the GPU, data can go from calculation to visible frame without a CPU round-trip.

Every code example on this page runs live in the RunMat sandbox. Click any example and modify it. No install, no account required.

Here is a tour of the plotting system in one runnable block (subplots, overlays, handles, 3D, and styling):

x = linspace(0, 4*pi, 200);

subplot(2, 2, 1);
h1 = plot(x, sin(x));
hold on;
h2 = plot(x, cos(x));
set(h1, 'LineWidth', 2);
set(h2, 'LineWidth', 2, 'LineStyle', '--');
legend('sin(x)', 'cos(x)');
title('Overlaid signals');
xlabel('Angle (rad)');
ylabel('Amplitude');
grid on;

subplot(2, 2, 2);
t = linspace(0, 2*pi, 120);
scatter(cos(t), sin(t), linspace(10, 200, 120), t, 'filled');
colorbar;
title('Per-point size and color');
axis equal;

subplot(2, 2, 3);
[X, Y] = meshgrid(linspace(-3, 3, 80));
contourf(X, Y, X.^2 - Y.^2, 20);
colormap('turbo');
colorbar;
title('Contour field');
xlabel('x');
ylabel('y');

subplot(2, 2, 4);
[X3, Y3] = meshgrid(linspace(-3, 3, 60));
R = sqrt(X3.^2 + Y3.^2) + 0.01;
Z = sin(3*R) ./ R;
surf(X3, Y3, Z);
shading interp;
colorbar;
view(45, 30);
title('3D surface');

And it goes further. This is a live GPU-rendered animation running in your browser: eight point sources superposing on a 300x300 grid, 80 frames.

N = 300;
x = linspace(-10, 10, N);
y = linspace(-10, 10, N);
[X, Y] = meshgrid(single(x), single(y));

% Gaussian envelope — smoothly damps the surface toward the edges
envelope = exp(-(X.^2 + Y.^2) / 25);

for t = 1:80
    Z = zeros(N, N, 'single');

    % Superpose 8 wave sources equally spaced around a circle
    for k = 1:8
        a = 2 * pi * k / 8;
        cx = 6 * cos(a);
        cy = 6 * sin(a);
        r = sqrt((X - cx).^2 + (Y - cy).^2) + 0.1;

        Z = Z + sin(5 * r - t * 0.2 + k * 0.8) ./ (1 + r);
    end

    Z = Z .* envelope;
    surf(x, y, Z);
    colormap("jet");
    shading("interp");
    drawnow;
end

How RunMat plotting compares

RunMat renders plots on the GPU via WebGPU with MATLAB-compatible syntax. MATLAB uses a CPU-bound Java figure system (MATLAB Online gives 20 hr/mo free). matplotlib rasterizes on the CPU and has limited browser support through Pyodide. Plotly renders via WebGL but uses a Python/JS API, not MATLAB syntax. The table below breaks this down by dimension:

DimensionRunMatMATLABmatplotlibPlotly
RenderingWebGPU compute + render shadersCPU, Java figure systemCPU, Agg rasterizerWebGL canvas
GPU data pathShared GPU pipelineCPU copy (gpuArray needs gather)CPU onlyCPU to WebGL upload
BrowserClient-side WebGPU, no serverServer-rendered (20 hr/mo free)Limited (Pyodide)Full WebGL
Animationdrawnow loop in browserDesktop onlyDesktop, slow on large dataCallback API
3D interactionOrbit/pan/zoom at frame rateBasic rotate3dSlow on large dataCan slow on large meshes
Figure persistenceScene state, replayable.fig (proprietary)Pickle (version-dependent)JSON
SyntaxMATLAB-compatibleNative MATLABPython APIPython/JS API

One honest limitation: RunMat covers around 20 plot-producing functions and another 15+ annotation and styling commands today. MATLAB has accumulated specialized chart types across dozens of toolboxes over 40 years. If you need something very specific (say, polarplot or geobubble), it may not exist in RunMat yet. The coverage grows with each release.

Choosing the right plot type

The right plot type depends on your data shape: use plot for continuous signals, scatter for unordered 2D points, surf or contourf for scalar fields over a grid, bar for category comparisons, and hist for distributions. The table below maps 13 common data patterns to the right RunMat function:

Data patternBest plot typeRunMat functionNotes
y vs. x, continuousLine plotplot(x, y)Connect points to show trends
y vs. x, discrete samplesStem plotstem(x, y)Vertical lines for sampled signals
Unordered 2D pointsScatterscatter(x, y)Per-point size/color supported
Category comparisonsBar chartbar(y)Grouped and stacked variants
Value distributionHistogramhist(data, nbins)Automatic or manual binning
Scalar field over 2D gridSurface or contoursurf(X,Y,Z) or contourf(X,Y,Z)shading interp for smooth color
Matrix as imageHeatmapimagesc(M)One pixel per element
3D path or trajectory3D lineplot3(x,y,z)Orbit/pan/zoom in viewer
3D point cloud3D scatterscatter3(x,y,z)Per-point color for fourth variable
2D vector fieldQuiverquiver(X,Y,U,V)Arrow direction and magnitude
Measurement uncertaintyError barserrorbar(x,y,err)Symmetric or asymmetric bounds
Cumulative areaFilled curvearea(x, y)Fill between curve and baseline
Part-to-wholePie chartpie(values)Use sparingly; bar is usually clearer
Multiple viewsSubplotssubplot(m,n,p)Mix any plot types in one figure

For a deeper treatment of when to pick surf over mesh, scatter over plot, or imagesc over contourf, see Choosing the Right Plot Type.


Start simple: your first plot

To create a line plot in RunMat, pass two vectors to plot(x, y). The first vector defines the x-axis, the second defines the y-axis. The default styling matches MATLAB: solid blue line, no markers. Add title, xlabel, ylabel, and grid on to label and annotate. This three-line pattern is the foundation for every plot in this guide.

x = 0:0.05:2*pi;
y = sin(x);
plot(x, y);
title('Sine wave');
xlabel('x');
ylabel('sin(x)');
grid on;

That is the entire workflow: create data, call plot, add labels. Everything below builds on this pattern.

Overlay and compare data

To plot multiple series on the same axes, call hold on after the first plot command, then call plot again for each additional series. RunMat automatically cycles through a default color palette so each line is visually distinct. Call legend to label them. This pattern works identically with scatter, bar, stem, and other plot types.

x = 0:0.05:2*pi;
plot(x, sin(x));
hold on;
plot(x, cos(x));
legend('sin(x)', 'cos(x)');
title('Sine and cosine');
xlabel('x');
ylabel('Amplitude');
grid on;

Style your figure with handles

plot returns a handle. Pass it to set to adjust line width, color, dash style, or other properties after creation. This is MATLAB's handle graphics model, and RunMat uses the same property names (LineWidth, Color, DisplayName).

x = linspace(0, 2*pi, 150);
h1 = plot(x, sin(x));
hold on;
h2 = plot(x, cos(x));
set(h1, 'LineWidth', 2, 'Color', [0.0 0.45 0.74]);
set(h2, 'LineWidth', 2, 'Color', [0.85 0.33 0.1], 'LineStyle', '--');
title('Formatted comparison');
xlabel('Angle (rad)');
ylabel('Value');
legend('sin', 'cos');
grid on;
box on;
xlim([0 2*pi]);
ylim([-1.3 1.3]);

Styling quick reference

CommandWhat it doesExample
titleSet figure titletitle('My Plot')
xlabel, ylabel, zlabelSet axis labelsxlabel('Time (s)')
legendLabel data serieslegend('sin', 'cos')
grid on, grid offToggle grid linesgrid on
box on, box offToggle axes box outlinebox on
xlim, ylim, zlimSet axis rangexlim([0 10])
colormapSet color palettecolormap('turbo')
colorbarAdd color scale referencecolorbar
viewSet 3D camera angleview(45, 30)
get, setRead/write handle propertiesset(h, 'LineWidth', 2)
hold on, hold offOverlay or replace plotshold on
shadingSurface shading modeshading interp

For the full styling model (coordinating plot objects, axes state, and multi-series palettes), see Styling Plots and Axes.

Build multi-panel layouts with subplot

subplot(m, n, p) divides a figure into an m-by-n grid and activates the p-th panel. Each subplot has its own axes state, so labels, limits, and grid apply only to the active panel. This is how engineers present related views of the same data side by side.

subplot(2, 2, 1);
x = linspace(0, 2*pi, 100);
plot(x, sin(x));
title('Line');
grid on;

subplot(2, 2, 2);
scatter(randn(1, 100), randn(1, 100));
title('Scatter');
grid on;

subplot(2, 2, 3);
bar([2 5 3 8 6]);
title('Bar');
grid on;

subplot(2, 2, 4);
t = linspace(-3, 3, 40);
[X, Y] = meshgrid(t, t);
Z = sin(X) .* cos(Y);
surf(t, t, Z);
title('Surface');

For the complete handle graphics model (how figures, axes, legends, and handles fit together in subplot workflows), see Graphics Handles.

Go 3D: surfaces and meshes

surf(x, y, Z) renders a shaded surface from gridded data. Create a coordinate grid with meshgrid, compute a height matrix, and render. Large surfaces (300x300 and beyond) render at frame rate because the GPU converts your data directly into triangle geometry without a CPU round-trip.

x = linspace(-3, 3, 60);
y = linspace(-3, 3, 60);
[X, Y] = meshgrid(x, y);
Z = sin(X) .* cos(Y);
surf(x, y, Z);
colorbar;
shading interp;
title('sin(x) * cos(y)');
xlabel('x');
ylabel('y');
zlabel('z');

Colormaps and camera angle

RunMat supports named colormaps including jet, turbo, and parula, among others. view(az, el) sets the camera angle.

x = linspace(-3, 3, 80);
y = linspace(-3, 3, 80);
[X, Y] = meshgrid(x, y);
R = sqrt(X.^2 + Y.^2) + 0.01;
Z = sin(3*R) ./ R + 0.5*cos(2*X) .* sin(2*Y);
surf(x, y, Z);
colormap('turbo');
shading interp;
colorbar;
view(45, 30);
title('Composite surface with turbo colormap');
xlabel('x');
ylabel('y');
zlabel('z');

Wireframe with contour projection

meshc draws a wireframe surface with contour lines projected onto the base plane, so you can read both the shape and the level structure of a scalar field at once.

x = linspace(-3, 3, 50);
y = linspace(-3, 3, 50);
[X, Y] = meshgrid(x, y);
Z = X .* exp(-X.^2 - Y.^2);
meshc(x, y, Z);
title('meshc: wireframe + contour projection');
xlabel('x');
ylabel('y');
zlabel('z');
colorbar;

Contour maps and heatmaps

contourf fills the regions between iso-level curves. imagesc maps a matrix directly to a color image, one pixel per element.

x = linspace(-3, 3, 80);
y = linspace(-3, 3, 80);
[X, Y] = meshgrid(x, y);
Z = X.^2 - Y.^2;
contourf(X, Y, Z, 20);
colormap('turbo');
colorbar;
title('Hyperbolic paraboloid contours');
xlabel('x');
ylabel('y');
[X, Y] = meshgrid(linspace(-3, 3, 60));
Z = sin(X.^2 + Y.^2) .* cos(X - Y);
imagesc(Z);
colorbar;
title('Interference pattern heatmap');
xlabel('Column');
ylabel('Row');

3D trajectories and point clouds

plot3 draws a 3D line through coordinate triples. scatter3 plots unconnected markers with optional per-point color. The 3D camera supports orbit, pan, and zoom-to-cursor.

t = linspace(0, 6*pi, 500);
x = cos(t);
y = sin(t);
z = t / (2*pi);
plot3(x, y, z);
xlabel('x');
ylabel('y');
zlabel('Turns');
title('Helix');
view(35, 25);
grid on;
n = 300;
x = randn(1, n);
y = randn(1, n);
z = randn(1, n);
c = sqrt(x.^2 + y.^2 + z.^2);
scatter3(x, y, z, 30, c, 'filled');
colorbar;
title('3D scatter with distance coloring');
xlabel('x');
ylabel('y');
zlabel('z');
view(40, 30);

For help choosing between surf, mesh, contour, and imagesc, see Choosing the Right Plot Type.

Animate with drawnow

To animate plots in RunMat, call your plotting command inside a for loop and use drawnow after each iteration to flush the frame to the screen. This works identically in the browser and on native. RunMat coalesces and throttles presentation, so if computation outruns the display it presents the latest revision rather than queueing a backlog. The wave interference animation at the top of this guide renders 80 frames of an 8-source, 300x300 surface this way.

By comparison, MATLAB Online is server-rendered, so real-time animation is limited by round-trip latency. matplotlib in Pyodide has limited animation support. Plotly uses a separate callback-based animation API rather than an imperative drawnow loop.

For the full drawnow/pause semantics and how figure state interacts with the event loop, see Plotting in RunMat.

Specialized plots

RunMat supports plot types for specific data patterns beyond the core plot, scatter, bar, and surf families. These include stem for discrete sampled signals, errorbar for measurements with uncertainty bounds, quiver for 2D vector fields, area for filled curves, and pie for part-to-whole proportions.

Discrete sequences with stem

stem draws a vertical line from the baseline to each data point with a marker at the top, the standard representation for sampled signals and impulse responses.

n = 0:15;
y = sin(n * pi / 4) .* (0.9 .^ n);
stem(n, y);
title('Damped discrete sinusoid');
xlabel('Sample index');
ylabel('Amplitude');
grid on;

Measurements with errorbar

errorbar(x, y, neg, pos) draws a marker at each point with whiskers showing uncertainty bounds.

x = 1:6;
y = [2.1 3.5 2.8 4.2 3.9 5.1];
err = [0.3 0.5 0.2 0.4 0.3 0.6];
errorbar(x, y, err, err);
title('Measurements with error bounds');
xlabel('Experiment');
ylabel('Result');
grid on;

Vector fields with quiver

quiver(X, Y, U, V) draws arrows at each grid point showing direction and magnitude.

[X, Y] = meshgrid(-2:0.4:2, -2:0.4:2);
U = -Y;
V = X;
quiver(X, Y, U, V);
title('Rotation field');
xlabel('x');
ylabel('y');
axis equal;

Area and pie

area fills the region under a curve. pie shows part-to-whole proportions.

subplot(1, 2, 1);
x = 1:7;
y = [1 3 2 5 4 3 2];
area(x, y);
title('Filled area');
xlabel('Day');
ylabel('Value');

subplot(1, 2, 2);
pie([35 25 20 15 5]);
title('Budget allocation');

How the GPU rendering pipeline works

When your data is computed on the GPU, most tools require copying it back to the CPU before plotting. MATLAB's gpuArray needs gather(), and the figure system renders on the CPU. RunMat avoids that round-trip. The compute accelerator and the plotter share a single GPU device and command queue, so GPU-resident data goes from computation to rendered frame on the same hardware:

Loading diagram...

This is how the wave interference animation runs smoothly in the browser: the compute and render pipelines share the same GPU context, so data stays close to where it is drawn.

Every figure in RunMat is a structured scene (plot objects, axes state, view configuration, annotations), not a raster image. Figures can be replayed from their scene state, and exported as visual output.

For scene persistence and replay, see Plot Replay and Export. For GPU residency details, see GPU Plotting and Residency.

Try it now

The fastest way to learn plotting is to modify a working example. The code below computes the SVD of a random matrix and plots the singular value spectrum. Paste it into the sandbox, change the matrix size, add noise, and see how the spectrum shifts.

A = randn(100, 50);
[U, S, V] = svd(A, 'econ');
sigma = diag(S);
plot(sigma, 'o-', 'LineWidth', 1.5);
title('Singular value spectrum');
xlabel('Index');
ylabel('Singular value');
grid on;

Open the RunMat sandbox and start plotting. No install, no sign-up.

For more depth, read the plotting documentation, explore the plotting sub-guides, or see the GPU acceleration guide. If you are evaluating alternatives to MATLAB, the MATLAB alternatives comparison covers performance, compatibility, and 15 other dimensions beyond plotting.

Frequently asked questions

Is RunMat plotting compatible with MATLAB syntax? Yes. RunMat supports plot, scatter, surf, subplot, bar, hist, contour, imagesc, plot3, scatter3, and more with MATLAB-compatible syntax. Plotting code written for MATLAB generally works in RunMat without changes.

How do I make a 3D surface plot? Use surf(X, Y, Z) where X and Y are coordinate vectors and Z is a height matrix. Add colormap, shading interp, and view to control appearance and camera angle.

How do I plot multiple lines on the same figure? Call hold on after your first plot command, then call plot again for each additional line. Use legend to label each series.

What is the difference between scatter and plot? plot connects points with lines, emphasizing continuity and trends. scatter draws individual markers at each point, emphasizing position and clustering. scatter also supports per-point size and color vectors.

Can I plot in the browser without installing anything? Yes. RunMat's sandbox runs plotting through WebGPU entirely in your browser. No install, no account, no server round-trip. Every code example in this guide is runnable directly in the sandbox.

How do I add a title, axis labels, and legend? Use title('text'), xlabel('text'), ylabel('text') after your plot command. Call legend('series1', 'series2') to label multiple series.

How do I save or export a plot? RunMat supports screenshot export from the figure window. Figures are stored as structured scene state that supports replay and export.

What plot types does RunMat support? RunMat supports around 20 plot-producing functions including plot, scatter, bar, hist, surf, mesh, contour, contourf, imagesc, quiver, plot3, scatter3, stem, errorbar, area, pie, and stairs, plus 15+ annotation and styling commands.

Enjoyed this post? Join the newsletter

Monthly updates on RunMat, Rust internals, and performance tips.

Try RunMat — free, no sign-up

Start running MATLAB code immediately in your browser.