Get started (Three.js)
This is a brief introduction to how to incorporate VerseEngine using Three.js and VerseEngine's simple library verse-three.
Alternatively, you can download the complete source code from here (github.com).
Step 0: Create the world
First, create a simple space in Three.js.
For more information, see the Manual for Three.js.
View source code
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1,viewport-fit=cover"/>
<style> body { margin: 0; overflow: hidden; }</style>
<script async src="https://cdn.jsdelivr.net/npm/[email protected]/dist/es-module-shims.min.js"></script>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
"three/examples/jsm/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/",
"./index": "./index.js"
}
}
</script>
<script type="module">
import * as App from "./index";
</script>
</head>
<body></body>
</html>
import * as THREE from "three";
import { Sky } from "three/examples/jsm/objects/Sky.js";
function setupScene() {
const renderer = new THREE.WebGLRenderer();
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
60,
window.innerWidth / window.innerHeight,
0.1,
100
);
camera.position.set(0.0, 1.6, 0);
scene.add(camera);
scene.add(new THREE.AmbientLight(0xffffff, 1.0));
{
const sky = new Sky();
sky.scale.setScalar(450000);
scene.add(sky);
const sun = new THREE.Vector3();
sun.setFromSphericalCoords(
1,
THREE.MathUtils.degToRad(60),
THREE.MathUtils.degToRad(180)
);
sky.material.uniforms["sunPosition"].value.copy(sun);
}
let ground;
{
ground = new THREE.Mesh(
new THREE.PlaneGeometry(50, 50, 1, 1),
new THREE.MeshLambertMaterial({
color: 0x5e5e5e
})
);
ground.rotation.x = Math.PI / -2;
scene.add(ground);
}
scene.add(new THREE.GridHelper(50, 50));
const animate = () => {
renderer.render(scene, camera);
};
renderer.setAnimationLoop(animate);
}
function main() {
setupScene();
}
main();
Step 1: Extend setupScene
Modify the setupScene
.
- Enables functions to be specified externally to be executed every frame.
- Returns variables such as Scene.
...
/**
* @param {Array<(number)=>void>} ticks - Array of functions to be executed every frame
*/
function setupScene(ticks) {
...
ticks ||= [];
const clock = new THREE.Clock();
const animate = () => {
const dt = clock.getDelta();
ticks.forEach((f) => f(dt));
renderer.render(scene, camera);
};
renderer.setAnimationLoop(animate);
return { scene, renderer, camera, ground};
}
function main() {
const ticks = [];
const { scene, renderer, camera, ground } = setupScene(ticks);
}
...
Step 2: Set up players and objects
...
function main() {
const ticks = [];
const { scene, renderer, camera, ground } = setupScene(ticks);
// Create Player
const cameraContainer = new THREE.Object3D(); // Head
cameraContainer.add(camera);
const player = new THREE.Object3D(); // Player Body
player.add(cameraContainer);
scene.add(player);
camera.position.set(0.0, 1.6, 0); // Set camera position to default head height
// Target to teleport with VR controller
const teleportTargetObjects = [ground];
// Ground and Obstacles
const collisionBoxes = [new THREE.Box3().setFromObject(ground)];
}
...
Step 3: Load VerseEngine
Load the ES Modules version of verse-three and the tree-vrm used inside the verse-three.
...
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
"three/examples/jsm/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/",
"@pixiv/three-vrm": "https://cdn.jsdelivr.net/npm/@pixiv/[email protected]/lib/three-vrm.module.min.js",
"verse-three": "https://cdn.jsdelivr.net/npm/@verseengine/[email protected]/dist/esm/index.min.js",
"./index": "./index.js"
}
}
</script>
...
Step 4: Connect with VerseEngine
You can prepare your own animation and avatar files, though, You can also download verse-three example and try it out quickly.
setup-verse.js
import * as VerseThree from "verse-three";
const VERSE_WASM_URL =
"https://cdn.jsdelivr.net/npm/@verseengine/[email protected]/dist/verse_core_bg.wasm";
const ENTRANCE_SERVER_URL = "https://entrance.verseengine.cloud";
// Animation files
const ANIMATION_MAP = {
idle: "./asset/animation/idle.fbx",
walk: "./asset/animation/walk.fbx",
};
// Avatar files
const range = (n) => [...Array(n).keys()];
export const PRESET_AVATARS = [
...range(3).map((i) => `f${i}`),
...range(3).map((i) => `m${i}`),
].map((n) => ({
thumbnailURL: `./asset/avatar/${n}.png`,
avatarURL: `./asset/avatar/${n}.vrm`,
}));
const DEFAULT_AVATAR_URL =
PRESET_AVATARS[Math.floor(Math.random() * PRESET_AVATARS.length)].avatarURL;
// WebRTC RTCIceServers
const ICE_SERVERS = [
{ urls: "stun:stun.l.google.com:19302" },
{ urls: "stun:stun1.l.google.com:19302" },
];
export const setupVerse = async (
scene, renderer, camera, cameraContainer, player,
collisionBoxes, teleportTargetObjects
) => {
const adapter = new VerseThree.DefaultEnvAdapter(renderer, scene,
camera, cameraContainer, player,
() => collisionBoxes,
() => [], // Wall or other obstacle (not specified in this case)
() => teleportTargetObjects,
isLowSpecMode: true, // Improve performance by omitting hair and other shaking
);
const mayBeLowSpecDevice = VerseThree.isLowSpecDevice();
const res = await VerseThree.start(adapter, scene,
VERSE_WASM_URL, ENTRANCE_SERVER_URL, DEFAULT_AVATAR_URL,
ANIMATION_MAP, ICE_SERVERS,
{
maxNumberOfPeople: mayBeLowSpecDevice ? 8 : 16,
maxNumberOfParallelFileTransfers: mayBeLowSpecDevice ? 1 : 4,
presetAvatars: PRESET_AVATARS,
}
);
return res.tick; // Return functions that need to be executed every frame
};
index.html
...
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
"three/examples/jsm/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/",
"@pixiv/three-vrm": "https://cdn.jsdelivr.net/npm/@pixiv/[email protected]/lib/three-vrm.module.min.js",
"verse-three": "https://cdn.jsdelivr.net/npm/@verseengine/[email protected]/dist/esm/index.min.js",
"./setup-verse": "./setup-verse.js",
"./index": "./index.js"
}
}
</script>
...
index.js
...
import { setupVerse } from "./setup-verse";
...
function main() {
const ticks = [];
const { scene, renderer, camera, ground } = setupScene(ticks);
// Create Player
const cameraContainer = new THREE.Object3D(); // Head
cameraContainer.add(camera);
const player = new THREE.Object3D(); // Player Body
player.add(cameraContainer);
scene.add(player);
camera.position.set(0.0, 1.6, 0); // Set camera position to default head height
// Target to teleport with VR controller
const teleportTargetObjects = [ground];
// Ground and Obstacles
const collisionBoxes = [new THREE.Box3().setFromObject(ground)];
setupVerse(
scene, renderer, camera, cameraContainer, player,
collisionBoxes, teleportTargetObjects
).then((tick) => {
ticks.push(tick);
});
}
...
Link to completed version, etc.
For more detailed usage, see verse-three reference.
Tips
WARNING
VerseEngine builds a network of users for each web page URL.
When using localhost or local IP (e.g. 192.168.xxx.xxx) for development, some URLs may connect to strangers.
Either assign a dedicated name to the IP address in the hosts file,
Alternatively, you can work around it by giving the html file a sufficiently long random filename.
# https://very-long-name12345678/
127.0.0.1 very-long-name12345678
TIP
To use all functions, you must communicate via HTTPS.
Examples of services that can easily support HTTPS for web servers
- Cloudflare - The Web Performance & Security Company
- Let's Encrypt - free, automated, and open certificate authority
- ZeroSSL - Free SSL Certificates and SSL Tools
Tools to create local development certificates
Example of starting HTTPS Server for local development on Mac
# Install mkcert using Homebrew
brew install mkcert
mkcert -install
# Create a certificate (change the IP address to match your environment)
mkdir cert
cd cert
mkcert localhost 127.0.0.1 192.168.10.2
cd ..
# Start the server
npx http-server -c-1 --ssl --key ./cert/localhost+2-key.pem --cert ./cert/localhost+2.pem -p 8080