Running Threejs in my website
Jorge Martínez Garrido
January 13, 2024
In this tutorial, I am going to show you how to use Three.js in your Hugo website. By the end of this tutorial, you should be able to get interactive scenes like this one:
Use your mouse rotate around previous scene. Use the scrolling wheel to zoom in or out.
Main objective
The main objectives are:
- Using Three.js without using npm package registry
- Easily include a JS file inside a Markdown post
Avoiding to install Three.js
It is possible to install Three.js by using a Content Delivery Network (CDN). These are servers hosting different files. In this case, these files are the ones for Three.js. From the official installation guidelines, it looks like https://unpkg.com/ is the desired CDN.
Thus, the first lines to include in our Markdown post should be:
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@<version>/build/three.module.js",
"three/addons/": "https://unpkg.com/three@<version>/examples/jsm/"
}
}
</script>
At the time of this post, I am using version 0.160.0.
The three/addons/
import allows to use popular utilities like OrbitControls
,
which allows for an orbit camera within the scene.
Creating a placeholder for a scene
Any Three.js scene needs an element to render. A canvas for drawing. This
element is usually a <div>
with some id
. This identifier is reference in the
JavaScript code to indicate the renderer where it must present the scene.
Add the following lines right after previous installation script:
<div id="threejs-container-hello"></div>
Creating a simple scene
A simple scene is provided now. This code renders the wireframe of a cube.
import * as THREE from 'three';
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xEEEEEE);
// Create a camera
const camera = new THREE.PerspectiveCamera(20, 4/3, 0.1, 1000);
camera.position.z = 5;
// Create a renderer
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById('threejs-container-hello').appendChild(renderer.domElement);
// Create a cube
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x000000, wireframe: true});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// Add animation
const animate = () => {
requestAnimationFrame(animate);
// Rotate the cube
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
};
// Create an animation loop
const animate_wired_cube = () => {
requestAnimationFrame(animate);
renderer.render(scene, camera);
};
animate_wired_cube();
Pay special attention to the following line in previous code:
document.getElementById('threejs-container').appendChild(renderer.domElement);
Note this is indicating the renderer to look for the previous placeholder we created.
Also, pay attention to the import
statements. Since we are using CDNs, these
are slightly different than the ones required when using Node modules.
Putting all together by creating a Hugo shortcode
Hugo shortcodes are amazing! These are HTML files that support templating. Pass arguments and generate dynamically your desired HTML content.
Thus, we can combine all previous codes into a shortcode named threejs.html
.
Store this shortcode in your layouts/shortcodes/
directory:
<!-- Three.js short code for linking JS code and rendering a scene -->
{{ $version := .Get "version" }}
{{ $id := .Get "id" }}
{{ $src := .Get "src" }}
<!-- Create a container for rendering the scene -->
<div id="threejs-container-{{ $id }}"></div>
<!-- Import Three.js -->
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@{{ $version }}/build/three.module.js",
"three/addons/": "https://unpkg.com/three@{{ $version }}/examples/jsm/"
}
}
</script>
<!-- Load the script -->
<script type="module" src="{{ $src }}"></script>
Note that this accepts three arguments:
- String
version
for indicating which files from the CDN must be used - String
id
for rendering multiple scenes within the same post - Path to the
src
file containing the JS code for the scene
For our example, use it as:
{{< threejs version="0.160.0" id="hello" src="hello.js" >}}
Which results in: