Show Sidebar Hide Sidebar

3D Surface Plots in R

How to make interactive 3D surface plots in R.

New to Plotly?

Plotly's R library is free and open source!
Get started by downloading the client and reading the primer.
You can set up Plotly to work in online or offline mode.
We also have a quick-reference cheatsheet (new!) to help you get started!

Version Check

Version 4 of Plotly's R package is now available!
Check out this post for more information on breaking changes and new features available in this version.

library(plotly)
packageVersion('plotly')
## [1] '4.9.1'

Basic 3D Surface Plot

library(plotly)
# volcano is a numeric matrix that ships with R
p <- plot_ly(z = ~volcano) %>% add_surface()

p

Surface Plot With Contours

library(plotly)
# volcano is a numeric matrix that ships with R
p <- plot_ly(z = ~volcano) %>% add_surface(
  contours = list(
    z = list(
      show=TRUE,
      usecolormap=TRUE,
      highlightcolor="#ff0000",
      project=list(z=TRUE)
      )
    )
  ) %>%
  layout(
    scene = list(
      camera=list(
        eye = list(x=1.87, y=0.88, z=-0.64)
        )
      )
  )

p

2D Kernel Density Estimation

kd <- with(MASS::geyser, MASS::kde2d(duration, waiting, n = 50))
p <- plot_ly(x = kd$x, y = kd$y, z = kd$z) %>% add_surface()

p

Configure Surface Contour Levels

This example shows how to slice the surface graph on the desired position for each of x, y and z axis. contours.x.start sets the starting contour level value, end sets the end of it, and size sets the step between each contour level.

x = c(1,2,3,4,5)
y = c(1,2,3,4,5)
z = rbind(
  c(0, 1, 0, 1, 0),
  c(1, 0, 1, 0, 1),
  c(0, 1, 0, 1, 0),
  c(1, 0, 1, 0, 1),
  c(0, 1, 0, 1, 0))


library(plotly)
p <- plot_ly(
  type = 'surface',
  contours = list(
    x = list(show = TRUE, start = 1.5, end = 2, size = 0.04, color = 'white'),
    z = list(show = TRUE, start = 0.5, end = 0.8, size = 0.05)),
  x = ~x,
  y = ~y,
  z = ~z) %>%
  layout(
    scene = list(
      xaxis = list(nticks = 20),
      zaxis = list(nticks = 4),
      camera = list(eye = list(x = 0, y = -1, z = 0.5)),
      aspectratio = list(x = .9, y = .8, z = 0.2)))

p

Multiple Surfaces

z <- c(
  c(8.83,8.89,8.81,8.87,8.9,8.87),
  c(8.89,8.94,8.85,8.94,8.96,8.92),
  c(8.84,8.9,8.82,8.92,8.93,8.91),
  c(8.79,8.85,8.79,8.9,8.94,8.92),
  c(8.79,8.88,8.81,8.9,8.95,8.92),
  c(8.8,8.82,8.78,8.91,8.94,8.92),
  c(8.75,8.78,8.77,8.91,8.95,8.92),
  c(8.8,8.8,8.77,8.91,8.95,8.94),
  c(8.74,8.81,8.76,8.93,8.98,8.99),
  c(8.89,8.99,8.92,9.1,9.13,9.11),
  c(8.97,8.97,8.91,9.09,9.11,9.11),
  c(9.04,9.08,9.05,9.25,9.28,9.27),
  c(9,9.01,9,9.2,9.23,9.2),
  c(8.99,8.99,8.98,9.18,9.2,9.19),
  c(8.93,8.97,8.97,9.18,9.2,9.18)
)
dim(z) <- c(15,6)
z2 <- z + 1
z3 <- z - 1

p <- plot_ly(showscale = FALSE) %>%
  add_surface(z = ~z) %>%
  add_surface(z = ~z2, opacity = 0.98) %>%
  add_surface(z = ~z3, opacity = 0.98)

p