Plot 3D surfaces from z = f(x, y), polar z = f(r, theta), or fully parametric equations. Exact evaluation via mathjs, real holes where a function is undefined, and OBJ or STL export for 3D printing.
Initializing in your browser…
You want to see a hemisphere, and to check that the plotter is telling you the truth about where the function stops existing.
Surface
z = sqrt(9 - x^2 - y^2) over x, y in -5 to 5 at 96 x 96
Result
A dome that ends at the circle of radius 3 defined points: 2,589 / 9,216 z range drawn: 0.05 to 3.00 6,627 grid points are undefined and left as holes
Outside the circle of radius 3 the square root has no real value, so those grid points have no height at all. A triangle is drawn only when all three of its corners are defined, which is why the mesh ends at the edge of the domain rather than spreading into a flat square at zero. The defined-point count makes the gap a number rather than something you have to notice, and OBJ or STL export carries exactly those triangles, holes included.
What does a multivariable function actually look like? Enter an expression and this tool renders the surface in three dimensions, with the mathematics done properly: expressions are parsed and compiled by mathjs, and points where the function has no value leave real holes in the mesh instead of being quietly flattened to zero. Three modes cover height fields z = f(x, y), polar surfaces z = f(r, theta), and fully parametric shapes given as x, y and z in terms of u and v, so a sphere, a torus or a Mobius strip are all within reach. Finished surfaces export as OBJ or STL for 3D printing.
The expression you type is parsed and compiled once by mathjs, then evaluated at every point on the sampling grid. That matters twice over. It is correct, because a real parser understands operator precedence, nested calls and the full function library rather than rewriting your text with string substitutions that can misread it. And it is fast, because compiling once and evaluating thousands of times is a different proposition from rebuilding the expression at every single vertex, which is what made animation stutter here before. Values are checked against reference arithmetic across a corpus of expressions and sample points, and agree exactly.
Undefined regions are the part most plotters get wrong. The square root of a negative number has no real value, and drawing it as zero paints a flat plane through the middle of what should be an opening, then presents that plane as though it were the function. Here each grid point is marked valid or not, and a triangle is drawn only when all three of its corners are defined. Plotting sqrt(9 - x^2 - y^2) over a square domain therefore gives a dome that stops at the edge of its circle, and the readout tells you exactly how many of the grid points exist. Clipping to the z range works the same way: points outside the range are removed rather than pinned to the boundary, so a tall function shows an opening instead of a false plateau.
Geometry buffers are allocated once per resolution and refilled in place. Nothing is disposed or rebuilt per frame, so animating the t variable runs smoothly rather than as a slideshow of freshly constructed meshes, and the frame rate is displayed so you can see the cost of a resolution change directly. The viewport is watched with a ResizeObserver, which removes an initialisation race that could leave the canvas sized to zero and permanently blank.
Malformed input is reported rather than silently swallowed. A syntax error names the character it failed at, an unknown symbol is caught by a probe evaluation before it can turn into thousands of failed vertex evaluations and a blank view, and an expression that evaluates to something other than a number says so. Beyond that: hover the surface for the coordinates under the cursor, labelled axis ticks give the picture a scale, six perceptual colour ramps map height to colour, and the whole configuration lives in the URL so a particular surface can be shared as a link. OBJ and STL export exactly the triangles on screen, holes included, so a printed model matches what you were looking at.
Visualize surfaces, saddle points and critical regions, with genuine holes where a function leaves its domain rather than a misleading flat sheet.
Explore potential surfaces and field magnitudes, animating the t variable to watch a wave propagate.
Plot a surface, then export it as STL. The exported mesh carries exactly the triangles on screen, so the print matches the picture.
Show a sphere, a torus and a Mobius strip built from the same three-expression form, and change one term to see what it does.
Raise the resolution. Because the expression is compiled once and only evaluated more times, the cost of a finer grid is evaluation, not parsing, and the frame-rate readout shows what you are paying.
Not currently. One surface is rendered at a time, and entering a new expression replaces it. Parametric mode covers many shapes that would otherwise need two height-field surfaces, such as a full sphere.
They become holes. Each grid point is tested, and a triangle is drawn only when all three corners are defined, so the mesh ends at the boundary of the domain. The defined-point count under the viewport tells you how much of the grid exists. Nothing is substituted with zero.
The z range sets the colour scale and the drawing bounds. With clipping on, points outside it are removed, so a steep function shows an opening. With clipping off, the surface is drawn in full and the colour ramp simply saturates. Neither setting flattens anything onto the boundary.
Anything mathjs understands: arithmetic and powers, sin, cos, tan and their inverses and hyperbolics, sqrt, abs, exp, log, atan2, floor, ceil, round, min, max, and the constants pi and e. Use x and y in cartesian mode, r and theta in polar mode, u and v in parametric mode, and t for time in any of them.
Yes. STL export writes a binary mesh of exactly the triangles you can see. Note that a height field is a surface, not a solid, so a slicer may want it thickened; the presets that produce closed shapes, such as the sphere and torus, print directly.
It works on most modern mobile browsers with WebGL support, and touch gestures handle rotation and zoom. Lower the resolution if the frame rate readout drops.
Every calculation runs locally in your browser. Your numbers and expressions are not transmitted or stored.