HOWTO · R

How to Create a 3D Surface Plot From (x,y,z) Coordinates

Create a 3D surface plot in R from scattered (x, y, z) observations by interpolating a grid with akima and plotting it with rgl.

On this page

To create a 3D surface plot in R from scattered (x, y, z) observations, estimate z values on a grid with akima::interp(), then draw that grid with rgl::persp3d(). Add the original observations with rgl::points3d() to compare the measurements with the estimated surface. Interpolation provides estimates between observations; it does not guarantee that the estimated surface matches an unknown underlying function.

Interpolate Z Values with akima::interp()

A surface renderer needs z values associated with a grid of x and y coordinates. Scattered observations do not usually form that grid, so interpolation estimates values at grid locations from the measured x, y, and z data. Keep the three input vectors aligned: values at the same index must describe the same observation. For example, x[i], y[i], and z[i] are one measured point.

The akima::interp() function accepts those coordinate vectors and returns x and y grid coordinates together with a matrix of interpolated z values. That structure is useful for plotting because the matrix entries correspond to positions across the returned coordinate grid. The estimates depend on the measurements and the interpolation method; they are not additional observations. Inspect the returned coordinates and matrix when adapting the example to your data, especially if your observations are sparse or unevenly distributed.

Install the required packages once with install.packages(c("akima", "rgl")). Load them in the R session with library(akima) and library(rgl), or call their functions with the package prefixes shown below. The interpolation and rendering functions are also written with explicit package prefixes in the example to make their origin clear.

Plot the Surface with rgl::persp3d()

Pass the interpolation result’s x coordinates, y coordinates, and z matrix to rgl::persp3d(). This draws the estimated grid as a surface. Then pass the original x, y, and z vectors to rgl::points3d() to overlay the measured points. Keeping both layers visible helps distinguish observed data from values estimated between observations.

The rgl view is interactive in a suitable graphics environment: rotate it to inspect the shape from another angle and zoom to examine details. How the view appears depends on the R session and graphics environment. In a non-interactive context, the display may not open as a desktop window; use an environment that supports rgl when you need to manipulate the view.

Complete Example: Interpolate and Plot a 3D Surface

Install akima and rgl once with install.packages(c("akima", "rgl")). With both packages available, the package workflow is surface <- akima::interp(x, y, z, nx = 3, ny = 3), followed by rgl::persp3d(surface$x, surface$y, surface$z) and rgl::points3d(x, y, z). The runnable example below uses a complete 3-by-3 set of sample observations, so base R can also draw the same grid when either optional plotting package is unavailable. For scattered measurements, use the akima::interp() call above to estimate grid values before plotting.

x <- c(0, 1, 2, 0, 1, 2, 0, 1, 2)
y <- c(0, 0, 0, 1, 1, 1, 2, 2, 2)
z <- c(0, 1, 0, 1, 2, 1, 0, 1, 0)

if (requireNamespace("akima", quietly = TRUE)) {
  surface <- akima::interp(x = x, y = y, z = z, nx = 3, ny = 3)
  z_grid <- surface$z
  x_grid <- surface$x
  y_grid <- surface$y
} else {
  z_grid <- matrix(z, nrow = 3, byrow = TRUE)
  x_grid <- y_grid <- 0:2
}

if (requireNamespace("rgl", quietly = TRUE)) {
  rgl::persp3d(x = x_grid, y = y_grid, z = z_grid, col = "lightblue")
  rgl::points3d(x = x, y = y, z = z, col = "red", size = 6)
} else {
  persp(x = x_grid, y = y_grid, z = z_grid, col = "lightblue")
}

cat("Grid dimensions:", nrow(z_grid), "x", ncol(z_grid), "\n")

The sample vectors describe nine observations on a regular grid. When akima is installed, the script obtains its grid from akima::interp(); otherwise, it uses the supplied regular-grid values directly. It uses rgl::persp3d() and rgl::points3d() when rgl is installed, with base R persp() as a drawing fallback. The command-line output is:

Grid dimensions: 3 x 3

For scattered measurements, install both packages and use the akima and rgl calls described above. The displayed grid values are estimates derived from the input data, while the overlaid points remain the original observations. The interactive view depends on the R session and graphics environment; use a graphics-enabled session when you need to rotate or zoom it. Keep x, y, and z entries aligned when replacing the sample vectors.