Mapping an image onto a 3D surface in Javascript

Hi everyone!

I am trying to map an image onto a surface in Javascript using Plotly. I have found a previous post asking for a solution in R and an example of how to do it in Python.

I have attempted to implement the Python solution in Javascript but it hasn’t worked for me. Does anyone know how to do this in Javascript or if it is possible?

Specifically, I am first creating a 3D scatter plot and then trying to project an image onto an xy-plane with a z-value of 0.

Here is my attempt.

<html>
  <head>
    <script src="https://cdn.plot.ly/plotly-2.2.0.min.js"></script>
  </head>

  <body>
    <div id="myDiv"></div>
    <script>
      //Create a 3d scatter plot
      let x_values = [90,80,50,100]
      let y_values = [100,200,40,80]
      let z_values = [11,8,15,3]

      let scatterplot = [{
        x: x_values,
        y: y_values,
        z: z_values,
        type: 'scatter3d',
        mode: 'markers'
      }]

      Plotly.newPlot('myDiv', scatterplot)

      // Create a blank 2d canvas
      let canvas = document.createElement('canvas')
      let context = canvas.getContext('2d')
      context.rect(0, 0, 400, 400)
      context.fill()

      // A promise that the image will be loaded
      let promise = new Promise(function(resolve, reject) {
        // Create an image
        let img = document.createElement("img")
        img.crossOrigin = "Anonymous"
        img.src = "https://raw.githubusercontent.com/cldougl/plot_images/add_r_img/vox.png"

        img.onload = function(){
          if (img.complete) {
            resolve(img)
          }
          else {
            reject(Error("Image Not Found."))
          }
        }
      })

      // After the promise is fulfilled, get the pixel data
      promise.then(
        function(img){
          // Draw the image onto the canvas
          context.drawImage(img, 0, 0)

          // Get the pixel data for the 400x400 square of pixels in the top left
          let imageData = context.getImageData(0, 0, 400, 400).data

          add_surface(imageData)
        }
      )

      add_surface = function(imageData){
        // Create an xy-plane @ z = 0
        let width = 400
        let height = 400
        let x_values = []
        let y_values = []
        let z_values = new Array(width).fill(0)

        for (let i = 0; i < width; i++) {
          // Fill with values 0 -> width
          x_values[i] = i
          // Fill with all zeros (width x height dimensions)
          z_values[i] = new Array(height).fill(0)
        }
        for (let j = 0; j < height; j++){
          // Fill with values 0 -> height
          y_values[j] = j
        }

        let surface = {
          x: x_values,
          y: y_values,
          z: z_values,
          type: 'surface',
          surfacecolor: imageData, // Surfacecolor is RGBA pixel data of image
          colorscale: [
            [0.0, 'rgb(0, 0, 0)'],
            [0.2, 'rgb(64, 64, 64)'],
            [0.4, 'rgb(122, 122, 122)'],
            [0.6, 'rgb(181, 181, 181)'],
            [0.8, 'rgb(226, 226, 226)'],
            [1.0, 'rgb(255, 255, 255)']
          ] // Simple grayscale color scale
        }

        // Add the surface to the scatterplot
        Plotly.addTraces('myDiv', [surface])
      }
    </script>
  </body>
</html>