Recreate the interactive depth portrait effect popularized by modern portfolio websites using Three.js and WebGL.
Before writing the scene, prepare the image assets. The whole effect depends on a small group of texture maps generated from one portrait.
The current project expects five files:
public/diffuse.png
public/alpha.png
public/depth.png
public/normal.webp
public/roughness.webp
These exact extensions are not required for the effect to work. I use both .png and .webp to show that multiple image formats can be used. In practice, .png and .webp are usually good choices for this kind of texture workflow.
The important rule is simple: the filenames and extensions you choose must match the paths you load later in Three.js.
Choose a photo that will survive background removal and depth-map generation.
A good source photo usually has:
Avoid very noisy images, low resolution screenshots or photos where the subject blends into the background.
The diffuse map is the visible portrait texture.
In this project, it is the image you actually see in the scene. My workflow used Canva, but Canva’s smart background remover is a premium feature. You can use any background-removal tool that gives you a clean subject with transparency.
For example, Adobe Express has a free online background remover:
A simple workflow is:
diffuse.png.Place it in public/diffuse.png.
The alpha map controls the portrait silhouette. White means visible, black means hidden.
To create it, start from the same background-removed image used for the diffuse map. The goal is to make the subject pure white and the background pure black.
There are many ways to do this:
One practical Canva workflow is:
alpha.png.The shader uses this map to keep the portrait edges clean and transparent.
The remaining maps help the shader create a stronger illusion:
depth.png tells the shader which parts feel closer or farther away. In a depth map, brightness usually represents distance information. The exact interpretation depends on the tool and shader, but the important part is that the face, body and background separation are readable.
normal.webp adds directional surface detail for subtle lighting. It usually has a purple/blue look because the colors represent surface direction rather than visible image color.
roughness.webp controls how soft or sharp small highlights feel. For this effect, it does not need to be physically perfect. It just needs to give the shader a useful grayscale guide for subtle light response.
You can generate these maps with free external tools. The exact tool can change over time, so treat these as options, not strict requirements:
The important idea is the output: create maps that align with the same portrait framing and export them with the names and formats you plan to load in the scene.
Before continuing, confirm that your chosen files exist. If you follow this project exactly, they will be:
public/diffuse.png
public/alpha.png
public/depth.png
public/normal.webp
public/roughness.webp
If you choose different extensions, update the future texture paths to match them. For example, if you export normal.png instead of normal.webp, the Three.js texture loader must also point to normal.png.
They should all describe the same image, with the same framing and proportions. If one map is cropped differently, the shader will still load it, but the depth and lighting will not line up.
Next: Building The Scene