Creating a scene

The goal of this section is to give a brief introduction to three.js. We will start by setting up a scene, with a spinning cube. A working example is provided at the bottom of the page in case you get stuck and need help.

Before we start

Before you can use three.js, you need somewhere to display it. Save the following HTML to a file on your computer, along with a copy of three.js in the js/ directory, and open it in your browser.

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>My first three.js app</title> <style> body { margin: 0; } canvas { display: block; } </style> </head> <body> <script src="js/three.js"</script> <script> // Our Javascript will go here. </script> </body> </html>

That's all. All the code below goes into the empty <script> tag.

Creating the scene

To actually be able to display anything with three.js, we need three things: scene, camera and renderer, so that we can render the scene with camera.

var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); var renderer = new THREE.WebGLRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); document.body.appendChild( renderer.domElement );

Let's take a moment to explain what's going on here. We have now set up the scene, our camera and the renderer.

There are a few different cameras in three.js. For now, let's use a PerspectiveCamera.

The first attribute is the field of view. FOV is the extent of the scene that is seen on the display at any given moment. The value is in degrees.

The second one is the aspect ratio. You almost always want to use the width of the element divided by the height, or you'll get the same result as when you play old movies on a widescreen TV - the image looks squished.

The next two attributes are the near and far clipping plane. What that means, is that objects further away from the camera than the value of far or closer than near won't be rendered. You don't have to worry about this now, but you may want to use other values in your apps to get better performance.

Next up is the renderer. This is where the magic happens. In addition to the WebGLRenderer we use here, three.js comes with a few others, often used as fallbacks for users with older browsers or for those who don't have WebGL support for some reason.

In addition to creating the renderer instance, we also need to set the size at which we want it to render our app. It's a good idea to use the width and height of the area we want to fill with our app - in this case, the width and height of the browser window. For performance intensive apps, you can also give setSize smaller values, like window.innerWidth/2 and window.innerHeight/2, which will make the app render at half size.

If you wish to keep the size of your app but render it at a lower resolution, you can do so by calling setSize with false as updateStyle (the third argument). For example, setSize(window.innerWidth/2, window.innerHeight/2, false) will render your app at half resolution, given that your <canvas> has 100% width and height.

Last but not least, we add the renderer element to our HTML document. This is a <canvas> element the renderer uses to display the scene to us.

"That's all good, but where's that cube you promised?" Let's add it now.

var geometry = new THREE.BoxGeometry(); var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } ); var cube = new THREE.Mesh( geometry, material ); scene.add( cube ); camera.position.z = 5;

To create a cube, we need a BoxGeometry. This is an object that contains all the points (vertices) and fill (faces) of the cube. We'll explore this more in the future.

In addition to the geometry, we need a material to color it. Three.js comes with several materials, but we'll stick to the MeshBasicMaterial for now. All materials take an object of properties which will be applied to them. To keep things very simple, we only supply a color attribute of >0x00ff00, which is green. This works the same way that colors work in CSS or Photoshop (hex colors).

The third thing we need is a Mesh. A mesh is an object that takes a geometry, and applies a material to it, which we then can insert to our scene, and move freely around.

By default, when we call scene.add(), the thing we add will be added to the coordinates (0,0,0). This would cause both the camera and the cube to be inside each other. To avoid this, we simply move the camera out a bit.

Rendering the scene

If you copied the code from above into the HTML file we created earlier, you wouldn't be able to see anything. This is because we're not actually rendering anything yet. For that, we need what's called a render or animate loop.

function animate() { requestAnimationFrame( animate ); renderer.render( scene, camera ); } animate();

This will create a loop that causes the renderer to draw the scene every time the screen is refreshed (on a typical screen this means 60 times per second). If you're new to writing games in the browser, you might say "why don't we just create a setInterval ?" The thing is - we could, but requestAnimationFrame has a number of advantages. Perhaps the most important one is that it pauses when the user navigates to another browser tab, hence not wasting their precious processing power and battery life.

Animating the cube

If you insert all the code above into the file you created before we began, you should see a green box. Let's make it all a little more interesting by rotating it.

Add the following right above the renderer.render call in your animate function:

cube.rotation.x += 0.01; cube.rotation.y += 0.01;

This will be run every frame (normally 60 times per second), and give the cube a nice rotation animation. Basically, anything you want to move or change while the app is running has to go through the animate loop. You can of course call other functions from there, so that you don't end up with a animate function that's hundreds of lines.

The result

Congratulations! You have now completed your first three.js application. It's simple, you have to start somewhere.

The full code is available below and as an editable live example. Play around with it to get a better understanding of how it works.

<html> <head> <title>My first three.js app</title> <style> body { margin: 0; } canvas { display: block; } </style> </head> <body> <script src="js/three.js"></script> <style> var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 ); var renderer = new THREE.WebGLRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); document.body.appendChild( renderer.domElement ); var geometry = new THREE.BoxGeometry(); var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } ); var cube = new THREE.Mesh( geometry, material ); scene.add( cube ); camera.position.z = 5; var animate = function () { requestAnimationFrame( animate ); cube..x += 0.01; cube..y += 0.01; renderer.render( scene, camera ); }; animate(); </style> </body> </html>
Import via modules

While importing three.js via script tags is a great way to get up and running fast, it has a few drawbacks for longer lived projects, for example:

  • You have to manually fetch and include a copy of the library as part of your project's source code
  • Updating the library's version is a manual process
  • When checking in a new version of the library your version control diffs are cluttered by the many lines of the build file

Using a dependency manager like npm avoids these caveats by allowing you to simply download and import your desired version of the library onto your machine.

Installation via npm

Three.js is published as an npm module, see: npm. This means all you need to do to include three.js into your project is run "npm install three"

Importing the module

Assuming that you're bundling your files with a tool such as Webpack or Browserify, which allow you to "require('modules')" in the browser by bundling up all of your dependencies.

You should now be able to import the module into your source files and continue to use it as per normal.

var THREE = require('three'); var scene = new THREE.Scene(); ...

You're also able to leverage ES6 import syntax:

import * as THREE from 'three'; const scene = new THREE.Scene(); ...

or if you wish to import only select parts of three.js library, for example Scene:

import { Scene } from 'three'; const scene = new Scene(); ...

Importable Examples

The core of three.js is focused on the most important components of a 3D engine. Many other components like loaders or controls are part of the examples directory. three.js ensures that these files are kept in sync with the core but users have to import them separately if they are required for a project. You can find in the examples/jsm directory an ES6 module version for almost all example files. If you install three.js via npm, you can import them like so:

import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';

Note: When using code from the examples directory, it's important that all files match the version of your three.js main file. For example, it's not acceptable to use GLTFLoader and OrbitControls from R96 together with three.js R103.

Browser Support

Overview

Three.js can use WebGL to render your scenes on all modern browsers. For older browsers, especially Internet Explorer 10 and below, you may have to fallback to one of the other renderers (CSS2DRenderer, CSS3DRenderer, SVGRenderer). Additionally, you may have to include some polyfills, especially if you are using files from the /examples folder.

Note: if you don't need to support these old browsers, then it is not recommended to use the other renderers as they are slower and support less features than the WebGLRenderer.

Browsers that support WebGL

Google Chrome 9+, Firefox 4+, Opera 15+, Safari 5.1+, Internet Explorer 11 and Microsoft Edge. You can find which browsers support WebGL at Can I use WebGL.

JavaScript Language Features or Web APIs Used in three.js

Here are some features used in three.js. Some of them may require additional polyfills.

Feature Use Scope Modules
Typed Arrays Source BufferAttribute, BufferGeometry, etc.
Web Audio API Source Audio, AudioContext, AudioListener, etc.
WebXR Device API Source WebXRManager
Blob Source FileLoader, etc.
Promise Examples GLTFLoader, DRACOLoader, BasisTextureLoader, GLTFExporter, VRButton, ARButton, etc.
Fetch Examples ImageBitmapLoader, etc.
File API Examples GLTFExporter, etc.
URL API Examples GLTFLoader, etc.
Pointer Lock API Examples PointerLockControls

Polyfills

Just import polyfills based on your requirements. Taking IE9 as an example, you need to polyfill at least these features:

  • Typed Arrays
  • Blob

Suggested polyfills

WebGL compatibility check

Even though this is becoming less and less of a problem, some devices or browsers may still not support WebGL. The following method allows you to check if it is supported and display a message to the user if it is not.

Add https://github.com/mrdoob/three.js/blob/master/examples/jsm/WebGL.js to your javascript and run the following before attempting to render anything.

if ( WEBGL.isWebGLAvailable() ) { // Initiate function or other initializations here animate(); } else { var warning = WEBGL.getWebGLErrorMessage(); document.getElementById( 'container' ).appendChild( warning ); }
How to run things locally

If you use just procedural geometries and don't load any textures, webpages should work straight from the file system, just double-click on HTML file in a file manager and it should appear working in the browser (you'll see file:///yourFile.html in the address bar).

Content loaded from external files

If you load models or textures from external files, due to browsers' same origin policy security restrictions, loading from a file system will fail with a security exception.

There are two ways to solve this:

  1. Change security for local files in a browser. This allows you to access your page as:
  2. file:///yourFile.html
  3. Run files from a local web server. This allows you to access your page as:
  4. http://localhost/yourFile.html

If you use option 1, be aware that you may open yourself to some vulnerabilities if using the same browser for a regular web surfing. You may want to create a separate browser profile / shortcut used just for local development to be safe. Let's go over each option in turn.

Run a local server

Many programming languages have simple HTTP servers built in. They are not as full featured as production servers such as Apache or NGINX, however they should be sufficient for testing your three.js application.

Servez

Servez is a simple server with a GUI.

Node.js http-server

Node.js has a simple HTTP server package. To install:

npm install http-server -g

To run (from your local directory):

http-server . -p 8000

Python server

If you have Python installed, it should be enough to run this from a command line (from your working directory):

//Python 2.x python -m SimpleHTTPServer //Python 3.x python -m http.server

This will serve files from the current directory at localhost under port 8000, i.e in the address bar type:

http://localhost:8000/

Ruby server

If you have Ruby installed, you can get the same result running this instead:

ruby -r webrick -e "s = WEBrick::HTTPServer.new(:Port => 8000, :DocumentRoot => Dir.pwd); trap('INT') { s.shutdown }; s.start"

PHP server

PHP also has a built-in web server, starting with php 5.4.0:

php -S localhost:8000

Lighttpd

Lighttpd is a very lightweight general purpose webserver. We'll cover installing it on OSX with HomeBrew here. Unlike the other servers discussed here, lighttpd is a full fledged production ready server.

  1. Install it via homebrew
  2. brew install lighttpd
  3. Create a configuration file called lighttpd.conf in the directory where you want to run your webserver. There is a sample here.
  4. In the conf file, change the server.document-root to the directory you want to serve files from.
  5. Start it with
  6. lighttpd -f lighttpd.conf
  7. Navigate to http://localhost:3000/ and it will serve static files from the directory you chose.

IIS

If you are using Microsoft IIS as web server. Please add a MIME type settings regarding .fbx extension before loading.

File name extension: fbx MIME Type: text/plain

By default, IIS blocks .fbx, .obj files downloads. You have to configure IIS to enable these kind of files can be download.

Other simple alternatives are discussed here on Stack Overflow.

Typescript setup

three.js is a JavaScript-based library. However, it's possible to use it in a TypeScript project, as the library exposes Declaration Files (d.ts files).

A minimal configuration is required for the TypeScript compiler to discover three.js types. You will need to set the moduleResolution option to node, and the target option to es6 or newer.

// Example of minimal `tsconfig.json` file { "compilerOptions": { "target": "es6", "moduleResolution": "node", }, "include": [ "./src/**/*.ts" ], }

Note: As of today, it's not yet possible to use three.js typings without using those two options.

Note: It happens that some declarations are incorrect and/or missing. Contributing to Declaration Files is really helpful for the community, making three.js typings better and more accurate.

How to use WebGL2

Starting with three.js R95, the engine supports rendering with a WebGL 2 context. By default three.js always uses a WebGL 1 context when creating an instance of WebGLRenderer. If you want use a WebGL 2 context, please have a look at the following workflow.

Workflow

Since WebGL 2 is not supported by all devices that support WebGL 1, it's important to check the respective availability. To do so, please include WebGL.js into your project.

import { WEBGL } from 'three/examples/jsm/WebGL.js';

Next, use a code similar to the following in order to perform the availability check.

if ( WEBGL.isWebGL2Available() === false ) { document.body.appendChild( WEBGL.getWebGL2ErrorMessage() ); }

Now it's time to create the renderer by applying the HTML5 canvas element and the respective WebGL 2 context to the constructor of WebGLRenderer. As a result, three.js will internally use the given context for rendering and automatically convert the built-in material's shader code to GLSL ES 3.00.

Since you are manually creating the WebGL 2 rendering context, you also have to pass in all necessary context attributes. Note: It's not possible to modify these attributes after the context has been created, so passing them to the WebGLRenderer won't have any effect.

var canvas = document.createElement( 'canvas' ); var context = canvas.getContext( 'webgl2', { alpha: false } ); var renderer = new THREE.WebGLRenderer( { canvas: canvas, context: context } );

Sometimes it is necessary to write custom shader code. Use the following code template as a basis for your own implementation. First, the GLSL ES 3.00 code.

<script id="vs" type="x-shader/x-vertex"> #version 300 es void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); } </script> <script id="fs" type="x-shader/x-fragment"> #version 300 es precision highp float; precision highp int; out vec4 out_FragColor; void main() { out_FragColor = vec4( 1.0 ); } </script>

Second, the corresponding material creation in JavaScript.

var material = new THREE.ShaderMaterial( { vertexShader: document.getElementById( 'vs' ).textContent.trim(), fragmentShader: document.getElementById( 'fs' ).textContent.trim() } );

Supported features

Right now, the engine does only support a subset of all existing WebGL 2 features. The following list provides an overview about what's already available in the latest version of three.js.

  • 3D Textures
  • 2D Texture Arrays
  • Multisampled Renderbuffers
  • Non-power of two (POT) textures work just the same as POT textures now. No resizing is required for best quality.
Drawing lines

Let's say you want to draw a line or a circle, not a wireframe Mesh. First we need to set up the renderer, scene and camera (see the Creating a scene page).

Here is the code that we will use:

var renderer = new THREE.WebGLRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); document.body.appendChild( renderer.domElement ); var camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 500 ); camera.position.set( 0, 0, 100 ); camera.lookAt( 0, 0, 0 ); var scene = new THREE.Scene();

Next thing we will do is define a material. For lines we have to use LineBasicMaterial or LineDashedMaterial.

//create a blue LineBasicMaterial var material = new THREE.LineBasicMaterial( { color: 0x0000ff } );

After material we will need a geometry with some vertices:

var points = []; points.push( new THREE.Vector3( - 10, 0, 0 ) ); points.push( new THREE.Vector3( 0, 10, 0 ) ); points.push( new THREE.Vector3( 10, 0, 0 ) ); var geometry = new THREE.BufferGeometry().setFromPoints( points );

Note that lines are drawn between each consecutive pair of vertices, but not between the first and last (the line is not closed.)

Now that we have points for two lines and a material, we can put them together to form a line.

var line = new THREE.Line( geometry, material );

All that's left is to add it to the scene and call render.

scene.add( line ); renderer.render( scene, camera );

You should now be seeing an arrow pointing upwards, made from two blue lines.

Creating text

There are often times when you might need to use text in your three.js application - here are a couple of ways that you can do so.

1. DOM + CSS

Using HTML is generally the easiest and fastest manner to add text. This is the method used for descriptive overlays in most three.js examples.

You can add content to a

<div id="info">Description</div>

and use CSS markup to position absolutely at a position above all others with a z-index especially if you are running three.js full screen.

2. Draw text to canvas and use as a Texture

Use this method if you wish to draw text easily on a plane in your three.js scene.

3. Create a model in your favourite 3D application and export to three.js

Use this method if you prefer working with your 3d applications and importing the models to three.js

4. Procedural Text Geometry

If you prefer to work purely in THREE.js or to create procedural and dynamic 3D text geometries, you can create a mesh whose geometry is an instance of THREE.TextGeometry:

new THREE.TextGeometry( text, parameters );

In order for this to work, however, your TextGeometry will need an instance of THREE.Font to be set on its "font" parameter. See the TextGeometry page for more info on how this can be done, descriptions of each accepted parameter, and a list of the JSON fonts that come with the THREE.js distribution itself.

Examples

WebGL / geometry / text WebGL / shadowmap

If Typeface is down, or you want to use a font that is not there, there's a tutorial with a python script for blender that allows you to export text to Three.js's JSON format: http://www.jaanga.com/2012/03/blender-to-threejs-create-3d-text-with.html

5. Bitmap Fonts

BMFonts (bitmap fonts) allow batching glyphs into a single BufferGeometry. BMFont rendering supports word-wrapping, letter spacing, kerning, signed distance fields with standard derivatives, multi-channel signed distance fields, multi-texture fonts, and more. See three-bmfont-text.

Stock fonts are available in projects like A-Frame Fonts, or you can create your own from any .TTF font, optimizing to include only characters required for a project.

Some helpful tools:

Loading 3D models

3D models are available in hundreds of file formats, each with different purposes, assorted features, and varying complexity. Although three.js provides many loaders, choosing the right format and workflow will save time and frustration later on. Some formats are difficult to work with, inefficient for realtime experiences, or simply not fully supported at this time.

This guide provides a workflow recommended for most users, and suggestions for what to try if things don't go as expected.

Before we start

If you're new to running a local server, begin with how to run things locally first. Many common errors viewing 3D models can be avoided by hosting files correctly.

Recommended workflow

Where possible, we recommend using glTF (GL Transmission Format). Both .GLB and .GLTF versions of the format are well supported. Because glTF is focused on runtime asset delivery, it is compact to transmit and fast to load. Features include meshes, materials, textures, skins, skeletons, morph targets, animations, lights, and cameras.

Public-domain glTF files are available on sites like Sketchfab, or various tools include glTF export:

If your preferred tools do not support glTF, consider requesting glTF export from the authors, or posting on the glTF roadmap thread.

When glTF is not an option, popular formats such as FBX, OBJ, or COLLADA are also available and regularly maintained.

Loading

Only a few loaders (e.g. ObjectLoader) are included by default with three.js β€” others should be added to your page individually. Depending on your preference and comfort with build tools, choose one of the following:

// global script <script src="GLTFLoader.js"></script> // commonjs var THREE = window.THREE = require('three'); require('three/examples/js/loaders/GLTFLoader'); // ES modules import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';

Once you've imported a loader, you're ready to add a model to your scene. Syntax varies among different loaders β€” when using another format, check the examples and documentation for that loader. For glTF, usage with global scripts would be:

var loader = new THREE.GLTFLoader(); loader.load( 'path/to/model.glb', function ( gltf ) { scene.add( gltf.scene ); }, undefined, function ( error ) { console.error( error ); } );

See GLTFLoader documentation for further details.

Troubleshooting

You've spent hours modeling an artisanal masterpiece, you load it into the webpage, and β€” oh no! 😭 It's distorted, miscolored, or missing entirely. Start with these troubleshooting steps:

  1. Check the JavaScript console for errors, and make sure you've used an onError callback when calling .load() to log the result.
  2. View the model in another application. For glTF, drag-and-drop viewers are available for three.js and babylon.js. If the model appears correctly in one or more applications, file a bug against three.js. If the model cannot be shown in any application, we strongly encourage filing a bug with the application used to create the model.
  3. Try scaling the model up or down by a factor of 1000. Many models are scaled differently, and large models may not appear if the camera is inside the model.
  4. Try to add and position a light source. The model may be hidden in the dark.
  5. Look for failed texture requests in the network tab, like C:\\Path\To\Model\texture.jpg. Use paths relative to your model instead, such as images/texture.jpg β€” this may require editing the model file in a text editor.

Asking for help

If you've gone through the troubleshooting process above and your model still isn't working, the right approach to asking for help will get you to a solution faster. Post a question on the three.js forum and, whenever possible, include your model (or a simpler model with the same problem) in any formats you have available. Include enough information for someone else to reproduce the issue quickly β€” ideally, a live demo.

FAQ

Which 3D model format is best supported?

The recommended format for importing and exporting assets is glTF (GL Transmission Format). Because glTF is focused on runtime asset delivery, it is compact to transmit and fast to load.

three.js provides loaders for many other popular formats like FBX, Collada or OBJ as well. Nevertheless, you should always try to establish a glTF based workflow in your projects first. For more information, see loading 3D models.

Why are there meta viewport tags in examples?

<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">

These tags control viewport size and scale for mobile browsers (where page content may be rendered at different size than visible viewport).

Safari: Using the Viewport

MDN: Using the viewport meta tag

How can scene scale be preserved on resize?

We want all objects, regardless of their distance from the camera, to appear the same size, even as the window is resized. The key equation to solving this is this formula for the visible height at a given distance:

visible_height = 2 * Math.tan( ( Math.PI / 180 ) * camera.fov / 2 ) * distance_from_camera;

If we increase the window height by a certain percentage, then what we want is the visible height at all distances to increase by the same percentage. This can not be done by changing the camera position. Instead you have to change the camera field-of-view. Example.

Why is part of my object invisible?

This could be because of face culling. Faces have an orientation that decides which side is which. And the culling removes the backside in normal circumstances. To see if this is your problem, change the material side to THREE.DoubleSide.

material.side = THREE.DoubleSide