Running Three.JS in VS Code Webview extension
When you are developing VS Code extension, you can use webview to display some content in the editor. You can use webview to display some HTML, CSS and JavaScript. You can also use webview to display some 3D content using Three.JS.
In this post, I will show you how to use webview to display 3D content using Three.JS.
Creating a webview
To create a webview, you need to create a webview panel. You can do that by calling the createWebviewPanel
method on the vscode
object:
import * as vscode from 'vscode';
// ...
const panel = vscode.window.createWebviewPanel(
'threejs',
'Three.JS',
vscode.ViewColumn.One,
{
enableScripts: true,
}
);
The first argument is the type of the webview. The second argument is the title of the webview. The third argument is the column where the webview should be displayed. The fourth argument is the options for the webview. In this case, we are enabling scripts in the webview.
Loading Three.JS
To load Three.JS in the webview, you need to add a script tag to the webview HTML:
panel.webview.html = `
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
</head>
<body>
</body>
</html>
`;
Rendering a scene
To render a scene, you need to create a scene, a camera and a renderer. You also need to add the renderer to the webview HTML:
panel.webview.html = `
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
</head>
<body>
<div id="threejs"></div>
<script>
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
</script>
</body>
</html>
`;
Then you need to add the renderer to the webview HTML:
panel.webview.html = `
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
</head>
<body>
<div id="threejs"></div>
<script>
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
document.getElementById('threejs').appendChild(renderer.domElement);
</script>
</body>
</html>
`;
Adding a cube
To add a cube to the scene, you need to create a cube and add it to the scene:
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
Rendering the scene
To render the scene, you need to call the render
method on the renderer:
renderer.render(scene, camera);
However, this will only render the scene once.
Adding an animation
To add an animation, you need to call the render
method on the renderer in a loop:
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
Adding a camera
To add a camera, you need to set the position of the camera:
camera.position.z = 5;
Adding a light
To add a light, you need to create a light and add it to the scene:
const light = new THREE.PointLight(0xffffff, 1, 100);
light.position.set(0, 0, 0);
scene.add(light);
Resizing the rendered
Resize once
To resize the rendered once, you need to set the size of the renderer:
renderer.setSize(window.innerWidth, window.innerHeight);
Resize on window resize
To add a resize listener, you need to add an event listener to the window:
window.addEventListener('resize', onWindowResize, false);
Then you need to create the onWindowResize
function:
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
Adding a rotation animation
To add a rotation animation, you need to add a requestAnimationFrame
call to the animate
function:
function animate() {
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
Now your final code should look like this:
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
const panel = vscode.window.createWebviewPanel(
'threejs',
'Three.JS',
vscode.ViewColumn.One,
{
enableScripts: true,
}
);
panel.webview.html = `
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
</head>
<body>
<div id="threejs"></div>
<script>
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
document.getElementById('threejs').appendChild(renderer.domElement);
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 5;
const light = new THREE.PointLight(0xffffff, 1, 100);
light.position.set(0, 0, 0);
scene.add(light);
function animate() {
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
window.addEventListener('resize', onWindowResize, false);
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
</script>
</body>
</html>
`;
}
export function deactivate() {}
Done
That's it.
Disclaimer
This page is made with help of Github Copilot and my minimal knowledge of Three.JS. I am not a 3D expert. If you have any suggestions, please let me know in the comments.
I am going to test this code now. I will let you know if it works.
Later I would do a video how this page was generated without me opening any documentation outside of VS Code.
Update
I have tested the code and it surprisingly works, I had to do some minor changes so that renderer resized right away as well as on resize event.
You can see the result in the gif below: