arrow-left

Only this pageAll pages
gitbookPowered by GitBook
1 of 46

Creator Tools

Loading...

Loading...

Loading...

Loading...

STANDALONE APP PUBLISHING

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

PlayCanvas Toolkit

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Publishing From Templates

Loading...

Loading...

Loading...

Introduction to Creator Tools

This page is the starting point for creators interested in learning about building on VIVERSE.


hashtag
The Best on the Web

VIVERSE is home to the best content on the internet that can be shared with audiences around the world through the web browser. Whether you are building engaging 3D experiences, web applications, or videos, your work can easily be hosted on and shared through VIVERSE. Our goal is to help creators distribute and monetize their work through VIVERSE's easy hosting and content distribution.

hashtag
Publishing to VIVERSE

Creators can host web-compatible content of all kinds on VIVERSE using , the one-stop-shop for managing content on VIVERSE. Through Studio, creators can upload new experiences, apps, and videos, and monitor the performance of content already on VIVERSE. In addition to Studio, we also offer a for creators uploading 3D experiences and applications to publish from the terminal (and even ).

circle-info

Learn on VIVERSE!

Engaging 3D experiences are the bread and butter of VIVERSE. As part of , the VIVERSE team has a rich legacy of empowering creators building in 3D and XR. As such, we have implemented several tools to help creators building 3D experiences.

Platform
Description
Documentation

hashtag
Sharing Content

VIVERSE makes it easy for creators to share work with audiences both on and outside of VIVERSE. After uploading their creations to VIVERSE, creators may select from multiple visibility levels, participate in content curation and discovery, and embed their VIVERSE-hosted content on any webpage of their choosing.

circle-info

on how to share your work on VIVERSE!

hashtag
Monetization

At VIVERSE, our goal is not only to help creators share their work, but also to help creators earn from their creations! We offer a variety of tools for creators to earn on VIVERSE, including engagement-based compensation, channel subscriptions, paid titles, and in-app purchases. Additionally, we frequently host competitions and programs where creators can participate and receive rewards and funding for building with VIVERSE.

circle-info

Learn more about !

hashtag
Optimization

The web browser is the most accessible platform for content sharing across the internet. Billions of devices, from mobile phones, to desktop computers, and VR headsets, have access to the browser! However, with so many potential users and devices, it is important to ensure that your content performs well for all of your intended users. Even if you do not have experience building for the web browser, we are here to help creators bring their work to the web for the first time!

circle-info

Learn more about !

hashtag
The Creator Community & Getting Support

VIVERSE is proud to support the work many of the most innovative artists, developers, educators, and entrepreneurs across the world. Our is the epicenter of our creator community and the best way to get connected with other innovative builders.

In the Discord Server, the community and VIVERSE team would love to hear your questions, bug reports, and ideas for making a more accessible platform. Simply create a new post in the #get-help channel when you land.

For issues related to your VIVERSE account, we recommend reaching out to the company-wide support channel:

Polygon Streaming

This document provides a guide that can be used to setup Polygon Streaming in a VIVERSE project.


hashtag
Media Polygon Streaming

hashtag
Streaming 3D Models into The Environment

Building with the PlayCanvas Toolkit

Learn how to build projects using common no-code functionality, as well as custom scripted entities and advanced APIs.


Create from our library of world templates and add 3D elements and media directly in VIVERSE. World decoration is great for meetup hosts and beginners looking to customize their first world from a wide range of template environments.

Standalone App Publishing

Developers may publish to VIVERSE using any platform that can build for WebGL/HTML5. Publish from UnityWebGL, Godot, ThreeJS, Babylon, AFRAME, and more!

See here

PlayCanvas Toolkit

PlayCanvas is an open source game engine with a web-based editor. We have created a toolkit for the PlayCanvas editor that allows creators to publish to VIVERSE with automatic support for multiplayer VIVERSE avatars.

See here

Studio
Command Line Interface
integrate publishing to VIVERSE directly into their toolsarrow-up-right
how to publish your project
HTCarrow-up-right
Check out our documentation
Community Discord Serverarrow-up-right
https://support.viverse.com/hc/en-usarrow-up-right

Create from Templates

circle-info

For Polygon Streaming using the PlayCanvas Extension, you must use the Polygon Streaming Asset ID url, not the link url.

The 3D models are visible in the PlayCanvas editor when utilizing Polygon Streaming.

The 3D model streaming into the environment with the avatar.

1

hashtag
Create the entity that will stream in the 3D model

A. Create a new entity.

B. Change the size of the model using the Scale.

C. Click the Edit Viverse Extension button.

2

hashtag
Add the PolygonStreaming module

A. In the VIVERSE extension, select the Media plugin for the Select plugins dropdown.

B. Select the PolygonStreaming module and add it.

Creating From Templates

This document details the process of creating a world from a template.


Templates are starter projects that are pre-built in VIVERSE. They are available on the profile page of your VIVERSE account.

Prerequisites

  • In order to create worlds you must first create a VIVERSE account.

1

hashtag
Navigate to

2

hashtag
Scroll down to "Worlds" and select the "Create World" button

3

hashtag
Select from the list of premade templates

4

hashtag
Updating the World Settings

Step 1. Select the Details page (A).

Step 2. Give the world a name in the Name field (B).

Step 3. Give the world a description in the Description

5

hashtag
Adding gameplay trailer and screenshots

Step 1. Click on the Media (A) tab.

Step 2. Add a trailer video to the Trailer (B) section.

Step 3. Add images to the Images (C) section.

6

hashtag
Adding a GitHub link

Step 1. Click on the Code & Collab (A) tab.

Step 2. Add a GitHub link to the GitHub (B) section.

7

hashtag
Managing World Settings after creating a world

Step 1. Select the Worlds tab (A).

Step 2. Select the 3 dots (B) on the world that needs to be updated.

Step 3. Click World Settings

Supported Media & Settings

This page details the different kinds of media that can be uploaded to a world in Edit Mode and the settings you can control.


hashtag
Adding Media

Media are added to VIVERSE worlds in Edit Mode using the toolbar at the bottom of the screen. Detailed below are the categories of assets that correspond with the buttons in this toolbar.

The Edit Mode asset upload toolbar
Marketplace
Upload
Embed/Link

hashtag
Media Settings

Once media have been uploaded, you are able to control several of its properties through the asset's details:

  • Asset Details | Here you can add an expandable description and a clickable link to an external url.

  • Transform Properties | Here you can control the asset's position, rotation, and scale.

  • Frame | For 2D media, such as images or external website links, you can add a colored frame to give your media extra dimensionality.

hashtag
Media Playback Settings

Configuring media playback settings for MP4s can be done via Audio (A) toggle.

Configuring media playback settings for MP3s can be done via Audio (A) toggle.

hashtag
Adding Interactions to VRMs

To add interactions to VRMs, click Add button (A) under Interactions. Configure the interaction in the panel (B).

Change Avatars Programatically

Control how users see and express themselves with the .changeAvatar() method on LocalPlayer.


If you don't want to use default VIVERSE avatars for your world, the PlayCanvas SDK features a simple method to swap to any .vrm avatar asset in your project.

, import the into your .mjs script, which has a of type LocalPlayer, which in turn has a .

NOTE: this script asset must be placed in /scripts or another subfolder, since it assumes the VIVERSE SDK is one level up, located at: "../@viverse/create-sdk.mjs"

Custom Code

The page introduces the basic information about implementing custom code with PlayCanvas in VIVERSE.


hashtag
Fundamentals

VIVERSE allows developers to use nearly any of the custom scripting interfaces , including but not limited to WebRequests like http & websockets, GLSL shaders, and 3rd party libraries imported directly into your project's hierarchy.

However, by default, VIVERSE's PlayCanvas SDK does automatically handles avatar transform and audio networking. For customizing any features related to the VIVERSE avatar, we provide an additional API reference (see VIVERSE SDK APIs below).

hashtag
VIVERSE SDK APIs

The documentation for VIVERSE's SDK APIs is currently housed here: https://viveportsoftware.github.io/pc-lib/arrow-up-right. We provide interfaces for customizing the default VIVERSE Cameras, Player, Network, and Environment, as well as the unique attributes when in an XR/immersive session in a VR headset.

These interfaces are automatically injected when you activate the VIVERSE Chrome Extension; The folder @viverse is added to your asset folder, which contains all interfaces in the create-sdk.mjs file. All interfaces can be imported and manipulated in your custom scripts.

circle-exclamation

For accessing the APIs in the VIVERSE SDK, you must use .mjs file types, which support import and export statements.

hashtag
A Note on No-Code Tools

In addition to adding the create-sdk.mjs file on initialization, the VIVERSE Chrome Extension adds create-extensions-sdk.mjs, which contains all of the interfaces between the no-code tools and PlayCanvas.

Within this file, you can see that each no-code function is assigned a unique trigger string, such as 'trigger:300006' for EntitySubscribeTriggerEnter. This is useful, because you can arbitrarily listen for and fire these exact same event names in custom scripts, connecting together the VIVERSE No-Code Functions and your custom code.

provided by PlayCanvasarrow-up-right

Playback Settings | For media with some form of playback, such as videos or streams, you can control audio and playback settings.

Import assets associated with your account which you have uploaded to or purchased from the VIVERSE marketplace.

Upload files to your world from your computer.

Embed assets hosted on servers external to VIVERSE.

GLB/glTF 2.0 files

JPG, PNG, GIF, PDF, MP4, MP3, GLB/glTF, VRM files

Websites, Images, Videos, Audio files, PDFs, .m3u8, YouTube videos/live streams

The media properties editor
C. Add the polygon streaming url. The URL should be in the following format: https://stream-stage.viverse.com/polygon_file/b9e62012-11e5-49cb-8ede-de596eec537e/aee7496b-b459-4e5d-87fe-2658955eb4f0/model.xrg
field (C).

Step 4. Upload a thumbnail of the world in the Thumbnail (D) section.

Step 5. Assign at least genre to the world in the Genre (E) section.

Step 6. Set the compatible devices types for the world in the Devices (F) section.

(C).
worlds.viverse.com/profilearrow-up-right
- or you can alter this import path as needed. For more information on how .mjs scripts and imports work, see
.

Because vrmAsset is defined as an attribute of type Asset, we can then select which asset to use directly in the PlayCanvas editor once the script is added to an entity.

When we publish to VIVERSE and load the experience, the .vrm is loaded immediately. Avatar switching for the local player is possible at any point during runtime and can be triggered with UI, trigger colliders, or with any other programmatic callback. Reference Code: PlayCanvas minimal reproduction projectarrow-up-right Live demoarrow-up-right

Per the API docsarrow-up-right
PlayerServicearrow-up-right
localPlayer propertyarrow-up-right
.changeAvatar() methodarrow-up-right
Introduction to MJS
See here

Camera Management: Settings and Switching

How to use VIVERSE SDK's CameraService to manage camera settings and switch between different VIVERSE and custom cameras.


VIVERSE provides a powerful camera system alongside its player controller and input scripting that allows developers to switch between different views and control various camera behaviors. This guide will show you how to implement camera switching in your VIVERSE project.

hashtag
Basic Camera Switching

The VIVERSE player rig creates several cameras and switches between them depending on your settings and/or world scripting: CAMERA_3RD, CAMERA_1ST, CAMERA_VR, CAMERA_ORBITAL are all separate in the scene hierarchy.

The provides methods to switch between different camera views provided by the VIVERSE avatar rig. Here's a basic .mjs script importing the CameraService and switching to first-person camera mode on init. Simply attach this script to any entity in your project.

NOTE: this script asset must be placed in /scripts or another subfolder, since it assumes the VIVERSE SDK is one level up, located at: "../@viverse/create-sdk.mjs" - or you can alter this import path as needed.

hashtag
Custom Camera Override Example

If the VIVERSE player rig cameras don't totally meet your needs, you can switch to your own camera entity for cut scenes or preview cameras. Here's a practical example of how to override the default VIVERSE camera with a custom camera on init:

hashtag
Camera Switching on Events: VR and Player Ready

You can listen for certain events from other services that may be related to your desired camera behavior. For example, on the player:ready event, we know that all player cameras and scripting have loaded in and are ready to interact with the scene:

hashtag
Additional Camera Properties

The CameraService provides several properties to customize camera behavior:

hashtag
Additional Resources

Open Brush

This page details how to publish to VIVERSE from the VR illustration tool Open Brush.


hashtag
Introduction

In December 2025, VIVERSE and the Icosa Foundation, Open Brush's steward, teamed up with digital artist SUTU to create a publishing pipeline for Open Brush creators to host, distribute, and monetize their work on VIVERSE. When publishing to VIVERSE, creators are able to share their work on Mobile, Desktop, and VR through the web browser, complete with networked VRM avatars and real-time chat. The publishing process is a simple and powerful way for Open Brush creators to expand their audiences and make their work more accessible!

hashtag
Publishing Tutorial

1

hashtag
Sign into VIVERSE from Open Brush

Within the Open Brush application in VR, navigate to the "Accounts" toolbar and select "VIVERSE" to open the browser window. Enter your VIVERSE account credentials and complete the sign in process, closing the browser window when prompted.

2

hashtag
Tips & Troubleshooting

chevron-rightWhat should I do after publishing?hashtag

After publishing, we encourage all creators to curate their and . These help with discoverability on VIVERSE.

We also encourage creators to take advantage of the easy sharing of your work on VIVERSE. You can quickly share your VIVERSE url with anyone and even embed it on your artistic website. If you post your world, tag @VIVERSEOfficial on all platforms and we will help share!

chevron-rightHow can I monetize my Open Brush work?hashtag

We want to help Open Brush creators earn from their awesome work on VIVERSE! Consult our and contact our team if you have any questions.

chevron-rightHow can I get help or request a new feature?hashtag

In the , we have a dedicated #open-brush channel where we want to hear your feedback, bug reports, and feature requests!

chevron-rightAre there any content restrictions on VIVERSE?hashtag

All Open Brush content must abide by the . In general, these terms restrict the depiction of NSFW content.

Sharing Content

This page shared details about how VIVERSE creators can share their work and get their work discovered on VIVERSE.


hashtag
Creator Profile

VIVERSE makes it easy for creators to share their work with audiences on and outside of VIVERSE. All content is associated with the creator profile, which can be customized to showcase additional media and details. The VIVERSE team manually curates creator profiles who actively publish and share their work, so be sure to fully customize your creator profile with details about your creative practice!

Your VIVERSE Creator profile has two visibility modes, public and private. In addition to your banner image, description, media, and content, private mode allows creators to view the content that your have purchased, the creators you follow, and other liked content.

hashtag
Discovery on VIVERSE

After your content has been approved and set to Public, it will automatically be discoverable based on its assigned Genres and Description details. The VIVERSE recommendation system also suggests content related to players' interests and based on a content's popularity (both likes and recent viewership).

In addition to algorithmically recommending content, the VIVERSE team manually curates and features content on our webpages which can help your work get seen by more players. The best way to get curated is to make the VIVERSE team aware of your work. We recommend sharing your project on social media and tagging @VIVERSEofficial, and to join our to show off your work in the #share channel.

hashtag
Embedding Content

The VIVERSE team supports creators who want to share their work across the internet, including embedding on other high-traffic webpages and curation surfaces. Underneath your content, the share button presents options for sharing via social media and through iframe embedding.

Iframes are a powerful and easy way to allow users experience content hosted on VIVERSE on other webpages. When you select Embed, you will receive an iframe html element that can be easily added to your webpage.

In some cases, you may need the VIVERSE team to manually approve the webpage you wish to embed on. To process this manual request, please with your content's URL, the URL of the webpage you want to embed your content on, and a description of the webpage.

Audio

This document provides a guide that can be used to setup audio and extend the functionality of audio in a VIVERSE project.


hashtag
Media Audio

hashtag
Adding Audio and Extending Audio Functionality

An audio clip has been added to the scene.

1

hashtag
Setup audio

A. Create a new entity.

B. Click the Edit Viverse Extension button.

2

Custom Loading Screens

Learn how to create a custom loading screen on VIVERSE during asset pre-loading.


hashtag
Default Loading Experience

Because web games and assets take time to transfer over the internet and load onto the player's device, user experience during loading is important. If shown a blank or broken-looking screen during this time, users may leave early, and never get to try the full experience.

While creators should optimize load time as much as possiblearrow-up-right, it's also somewhat inevitable. As such, VIVERSE provides a default loading experience during the asset pre-load phase:

hashtag
Custom Loading Screens

However, some creators may wish to make custom loading screens more in line with the aesthetics of their game or app. This can help sell an overall sense of quality and immediately set the tone for users.

VIVERSE creators can make full use of . Just navigate to PlayCanvas Project Settings > Loading Screen and click "Create Default."

If you have the installed, this will inject a VIVERSE logo into the loading screen template, which is required for all projects.

Other than that, loading-screen.js is yours to customize. Add your logo, a tagline in a custom font, or a cool background image, grounding your users in your experience immediately.

hashtag
Control Which Assets Download During the Loading Screen

All assets with the "Preload" box checked in asset settings will be downloaded during the loading screen phase. Limiting pre-loaded assets can help get users to your game view faster, which will result in fewer overall "bounces" from your game (meaning users who quit the game during loading). So only check "Preload" if the asset is immediately needed - otherwise it will stream in behind the scenes as needed, .

Scene Settings

This page overviews the interface for controlling global settings for your VIVERSE world in PlayCanvas.


hashtag
Introduction

The VIVERSE scene settings interface is used to control several settings for your entire VIVERSE world. The interface can be opened by clicking on the triangle button that is added to the left-hand toolbar when the extension is enabled:

hashtag
Polygon Streaming

The Polygon Streaming settings control how all assets embedded with VIVERSE's service are rendered in your world. For details on these settings, please consult this documentation:

hashtag
Quest Config

The Quest Config settings allow you to create and add steps to Quests in your world. For details on these settings, please consult this documentation:

hashtag
Post Effect

The Post Effect settings allow you to add and control the values of post-processing effects on your avatar's camera. For more details on the specific post effects and their values, please consult this documentation:

VIVERSE Studio

This page details important information about using the VIVERSE Creator Studio to publish and manage settings for content associated with your account.


hashtag
Introduction

The is the one-stop-shop for managing all content associated with your VIVERSE account and profile. Studio includes analytics about the performance of your content and tools to manage both video and application content.

Video

This document provides a guide that can be used to setup videos and extend the functionality of videos in a VIVERSE project.


hashtag
Media Video

hashtag
Adding Video And Extending Video Functionality

Pick and Throw

This document provides a guide that can be used to setup an entity that can be picked up and threw out. Users in the World can pick the object with "G", Throw it with "T", and put it down with "H".


hashtag
Pick and Throw

This guide provides instructions for setting up the pick and throw object your Scene.

1

import { Script, Asset } from "playcanvas";
import { PlayerService } from "../@viverse/create-sdk.mjs";

export class VvSwitchAvatars extends Script {
  static scriptName = "vvSwitchAvatars";

  /**
   * @attribute
   * @type {Asset}
   */
  vrmAsset = null;

  initialize() {
    this.playerService = new PlayerService();
    this.playerService.localPlayer.changeAvatar(this.vrmAsset);
  }
}
discord serverarrow-up-right
email the VIVERSE teamenvelope
PlayCanvas' built-in loading screen featurearrow-up-right
VIVERSE-PlayCanvas browser extensionarrow-up-right
per PlayCanvas' docsarrow-up-right
The custom loading screen from In Tirol.

Trigger & Action

Explore all common types of Triggers and Actions implemented in the PlayCanvas Toolkit.



Media

Explore all common types of Media supported by the PlayCanvas Toolkit.



No Code Tools

Explore all major building blocks of no-code functionality provided by the VIVERSE PlayCanvas Toolkit.


CameraServicearrow-up-right
VIVERSE SDK API Documentationarrow-up-right
PlayCanvas Scripting Guidearrow-up-right
import { Script } from "playcanvas";
import { CameraService, CameraTypes } from "../@viverse/create-sdk.mjs";

export class CameraManager extends Script {
  initialize() {
    this.cameraService = new CameraService();

    // Switch to first person view to first person 
    this.cameraService.switchPov(CameraTypes.PovTypes.FirstPerson);
    // At any time, you can switch back to CameraTypes.PovTypes.ThirdPerson

    // Prevent users from switching POV, which is usually done in user settings or with the keyboard shortcut "V"
    this.cameraService.canSwitchPov = false;
  }
}
import { Script, Entity } from "playcanvas";
import { CameraService } from "../@viverse/create-sdk.mjs";

export class OverrideViverseCamera extends Script {
  /**
   * @attribute
   * @type {Entity}
   */
  overrideCamera = null;
  
  initialize() {
    if (!this.overrideCamera) {
      console.error("No override camera set!");
      return;
    }

    this.cameraService = new CameraService();
    this.cameraService.switchCamera(this.overrideCamera);
  }
}
// Wait for the XR start event then get the VR camera
this.app.xr.on("start", () => {
  // The VIVERSE player rig will switch to its "CAMERA_VR" automatically
  // So getting reference to the activeCamera at this time will point to
  // the "CAMERA_VR" entity
  const vrCamera = this.cameraService.activeCamera;
  
  // You could also search for this by name
  // const vrCamera = this.app.root.findByName("CAMERA_VR");
  
  // Then raycast from the forward vector, apply scripts, etc.
});

// Or wait for player to be ready before switching cameras
this.playerService.localPlayer.on("player:ready", () => {
  this.cameraService.switchCamera(this.previewCamera);
});
// Set minimum and maximum zoom distances
this.cameraService.minZoomDistance = 1;
this.cameraService.maxZoomDistance = 10;

// Disable camera zooming
this.cameraService.canZoom = false;

// Disable camera rotation
this.cameraService.canRotate = false;

// Get current POV type
const currentPov = this.cameraService.pov; // Returns PovTypes enum value

// Get active camera entity
const activeCamera = this.cameraService.activeCamera; // Returns null | Entity

hashtag
Upload to VIVERSE

Navigate to the "Upload" tab and select VIVERSE. Wait for the uploading process to complete.

3

hashtag
Test and Finish Publishing on VIVERSE Studio

After uploading, a web browser window will open with studio.viverse.com. From here, sign into VIVERSE if necessary, and finish accessing and testing your world.

world settings
creator profile
Monetization Docs
VIVERSE Discord Serverarrow-up-right
VIVERSE Terms and Conditionsarrow-up-right

hashtag
Add the audio module

A. In the VIVERSE extension, select the Media plugin for the Select plugins dropdown.

B. Select the Audio module and add it.

C. Add the audio to the Asset field.

D. Uncheck the auto play property to prevent the audio from starting when the avatar enters the environment.

hashtag
Studio Dashboard

The main dashboard of the VIVERSE studio includes metrics on how users engage with your content. With an adjustable timeframe and content filer, you may explore the number of visitors, both new and unique, as well as the number of views and likes your content receives.

hashtag
Worlds

The worlds page includes tabs for managing existing content and uploading new content. The Content Management page contains links to edit a project's settings, including its visibility, description, and associated media. The Upload tab allows you to create new Worlds, upload and update files for existing worlds.

When updating content through the Upload tab, you will see a new, testable version with a unique link that can be merged into the main version/link for your content after submitting for review. You may also delete or hide Worlds through these settings.

hashtag
Videos

The Videos tab allows you to explore, upload, and manage settings of uploaded videos. Currently, it is not possible to upload a new version of a video to an existing link; you must delete the current version and upload a new file.

hashtag
Content Settings

Your content's settings can be edited by clicking the kebab menu next to your world/video.

Image showing to open a world's settings
The world settings configuration panel

Worlds can be assigned a genre, which impacts curation and discovery on the VIVERSE webpages.

With the world settings, you can configure your world's name, description, thumbnail, and accessibility settings. Both Genre and Access will influence how your world is categorized and displayed on the VIVERSE world discovery page.

Worlds can also be assigned a device filter, regulating which user devices are intended to experience the world based on its optimization.

Worlds can be set to one of three accessibility states and can be password protected to add an additional level of security.

hashtag
Review & Content Moderation

After uploading, your content will be viewable in preview mode only. To submit your world for curation on our webpages, please select "Submit for Review" and select whether you would like iframe support for your experience.

circle-info

Since VIVERSE content is iframed by default on our landing pages, several web features require requesting permissions, such as xr-spatial-tracking, camera, or allow-pointer-lock. These can be applied to preview links in the "iframe Support for Preview" menu, and must be set when you "Submit for Review," as well.

All content must comply with the standards outlined in the VIVERSE Platform Terms of Use: https://www.viverse.com/terms-of-usearrow-up-right While not an exhaustive list, we will not tolerate content which...

  • Depicts graphic or disturbing nudity, gore, sexual acts, violence, whether legal or illegal

  • Incites violence/hatred against or exposes personal information of individuals or groups

  • Includes malware, inappropriately collects user data, or inappropriately solicits money/information from users

  • Includes unauthorized connections with technologies that go against the VIVERSE terms of use

VIVERSE studioarrow-up-right

The user uses the mouse to hover over the video to access the controls.

The video is playing.

1

hashtag
Setup video

A. Create a new entity

B. Resize the video using the Scale.

C. Click the Edit Viverse Extension button.

2

hashtag
Add the video module

A. In the VIVERSE extension, select the Media plugin for the Select plugins dropdown.

B. Select the Video module and add it, then select either "Asset" for files contained in your PlayCanvas project, or "URL" for a video to stream, such as a YouTube URL (but please note: YouTube or other embeds are run inside iframe elements, which do not render in VR). For this example, choose "Asset."

hashtag
Create an entity that you wish to implement pick and throw.

A. Create an new entity

B. Setup collision component

C. Setup render component

D. Setup rigidbody and select "Dynamic" in Type

2

hashtag
Setup Pick and Throw mechanism

A. Select "PickAndThrow" in Select plugins

B. click "+" button

C. adjust throwing force level

Polygon Streamingarrow-up-right
Quests
https://developer.playcanvas.com/user-manual/graphics/posteffects/arrow-up-right

How to Publish

This page details the two methods to publish to VIVERSE: VIVERSE Studio and our command line interface.


hashtag
Introduction

You can upload to VIVERSE from all web-compatible frameworks, including 3D experiences, web applications, and videos! No matter what you are building with, you can upload and manage your builds easily using VIVERSE Studio or our command-line interface (CLI). Once uploaded, VIVERSE content is associated with your VIVERSE account and profile located at https://worlds.viverse.com/profilearrow-up-right. This page guides you through the uploading process using both methods!

circle-exclamation

While 3D experiences and applications can be uploaded using either VIVERSE Studio or the CLI, currently videos can only be uploaded via Studio, not the CLI.

hashtag
Publishing with VIVERSE Studio

The VIVERSE Studio offers a user-friendly interface that makes it easy for creators of all skill levels to upload and manage content on VIVERSE. You can learn the process for uploading or below.

hashtag
Publishing Apps with VIVERSE Studio

circle-info

Learn more about , including supported file types, requirements, and integrations with our SDKs.

1

hashtag
Begin Upload

Open VIVERSE Studio, navigate to the Worlds section, select the Upload tab, and click Create New World.

hashtag
Publishing Videos with VIVERSE Studio

circle-info

Learn more about , including upload limits, output specs, and our transcoding matrix.

1

hashtag
Upload your video file

Open VIVERSE Studio, navigate to Videos section, and drag-n-drop your video file to start uploading. As alternative, click Upload button at the right upper corner.


hashtag
Publishing with the CLI

The can be used to publish any web-compatible WebGL / HTML5 build to the VIVERSE platform after an authentication process.

hashtag
Installation

Using npm:

Note: This CLI requires Node.js version 22.15.0 or higher.

hashtag
Authentication

Login to VIVERSE platform:

Or directly pass in login credentials for CI/CD integration:

In such CI/CD environments, it's recommended to use environment variables:

After login, check your authentication status:

And to logout:

After , the VIVERSE CLI can be used to publish any working WebGL build to the VIVERSE platform after an authentication process. When publishing, you'll either access your existing projects, or create a new one.

hashtag
Creating Applications

You can use the VIVERSE CLI to create a new application directly:

Or specify a name:

Alternately, you can use the workflow to create an application ID:

hashtag
Listing Applications

Once authenticaed, you can view your account's available application list:

The output will be displayed in a table format with the following columns:

  • ID: Application identifier

  • STATE: Application state

  • TITLE: Application name

  • URL: Application preview URL

hashtag
Publishing

Publishing content requires two inputs:

  1. App ID — the target application to publish to (required)

  2. Content path — the directory containing your content (optional if you're already in that directory)

Option 1: Specify content path

Option 2: From within content directory

Note: The App ID is required. You can use the viverse-cli app list command to query your existing application IDs, or view the IDs of newly created applications after using app create.

Important: After uploading content successfully, you'll need to visit the Creator Studio website to complete the review and publishing process.

Warning: The <path> parameter MUST point to your build output folder and NOT your source code folder. Publishing source code folders (containing src/, node_modules/, or development files like .tsx, .jsx, .vue, .unity, etc.) will result in non-functional content and deployment failures.


hashtag
Specifications for Video Content

hashtag
Input video specifications

Feature
Supported Formats and Types

hashtag
Output video specifications

Resolution
Codec / Bitrate
Audio Specs
Description

hashtag
Transcoding matrix

If You Upload (Input)
Your Viewers Get These Resolutions and Frame Rates
Max Audio Quality
Quality Badge

Quests

This document provides a guide that can be used to setup a Quest-based system in a VIVERSE project.


hashtag
Quest System

hashtag
Understanding the Quest System

circle-info

Currently, the Quest system is unavailable for testing in Preview mode and can only be seen after clicking the Create World button in Preview mode.

In the sample app below, the trigger area is outlined in blue. Once the avatar enters the trigger area, the Quest system starts.

The first task requires the user to click the red box. Once the user clicks the red box, the first task is completed and the Quest system updates.

The second task requires the user to click multiple green boxes. Each green box that is clicked adds progress toward completing the task. Once all green boxes have been clicked, the task is completed and the Quest is finished.

If the user clicks a blue box during the quest, the Quest system resets. The user will need to re-enter the trigger area to restart the quest.

hashtag
How to Create a Quest System

1

hashtag
Open Viverse Scene Settings

A. Click on the Viverse Scene Settings button.

2

Avatar Teleport & Checkpoint

This document provides guides that can be used to change the spawn location of an avatar and transfer an avatar to a different location. These actions can be configured to execute with triggers.


hashtag
EntityCheckPoint

Create An Action That Sets A New Spawn Point

This guide provides instructions for setting up the EntityCheckPoint action. In the sample app, once the avatar enters one of the green trigger areas, the area becomes the new spawn location.

In this example, a trigger area is created and when triggered, an action sets a new spawn location. Any object can be used as a trigger, as long as the object has a collision component.

1

hashtag
Create a trigger area

A. Create a new Sphere entity.

B. The Collision component is not required for EntityCheckPoint action to work. The Collision component is required for the EntitySubscribeTriggerEnter trigger that will be used in this example.

hashtag
TeleportAvatar

Create An Action That Teleports An Object To A Specific Location

This guide provides instructions for setting up the TeleportAvatar action. In the sample app, once the avatar enters one of the green cylinder trigger areas, the avatar is teleported to another location.

In this example, a trigger is created and when the avatar or other objects enter the trigger area, an action teleports the avatar to a specific location. Any object can be used as a trigger, as long as the object has a collision component.

1

hashtag
Create a trigger area

A. Create a new Sphere entity.

B. The Collision component is not required for TeleportAvatar action to work. The Collision component is required for the EntitySubscribeTriggerEnter trigger that will be used in this example.

Seat

This document provides a guide that can be used to add a seat to objects in VIVERSE project. This allows the avatar to sit down.


hashtag
Seat

hashtag
Add functionality to allow avatar to sit down

1

hashtag
Add the chair

A. In this example, the chairs and table have been created in the scene under a single entity.

B. Click the Edit Viverse Extension button.

Options for accessibility settings
Configuring password settings
C. Add the video to the Asset field by selecting it within your project.

D. Uncheck the auto play property to prevent the video from starting when the avatar enters the environment.

Worlds are containers for content on VIVERSE and, once created, you can upload your 3D experience or application to your World.
2

hashtag
Complete Upload Details

Enter a Name and Description for your project and agree to VIVERSE's Platform Agreement.

Once completed, select Create.

3

hashtag
View the Overview Tab

Once your World is created, you can view and adjust settings for your content in the Overview Tab, such as its Name and App ID.

The App ID is an important piece of information for anyone into their project.

4

hashtag
Upload a .zip File

Within the Content Versions Tab, drag-and-drop or click the Select File button to begin the upload process.

Select your .zip file and click Upload.

5

hashtag
(Optional) Add Additional Permissions

Many projects may want additional permissions, allowing access to advanced features, such as the ability to download content from your application or open a new tab:

Content Behavior Permissions

  • allow-forms - Submit forms

  • allow-modals - Open modal dialogs

  • allow-popups - Open popup windows

  • allow-top-navigation - Navigate top window

  • allow-pointer-lock - Use pointer lock

  • allow-presentation - Start presentations

  • allow-downloads - Download files

  • allow-orientation-lock - Allow orientation lock

  • allow-popups-to-escape-sandbox - Allow popups to escape sandbox

  • allow-top-navigation-by-user-activation - Allow top navigation by user activation

Device and Sensor Access Permissions

  • accelerometer - Allows access to accelerometer sensor data

  • camera - Allows access to device camera

  • gyroscope - Allows access to gyroscope sensor data

Select Apply iframe Settings to apply your desired settings.

6

hashtag
Preview and Test Content

Select Preview for a link that is only accessible to the account that created the world.

Select Guest Preview for a 15-minute link that you may share with any other testers.

7

hashtag
Submitting for Review

Once you are satisfied with your creation, select Submit for Review to submit your world for moderation. Moderation usually takes less than 48 hours. If you need support with moderation, please reach out to us through this formarrow-up-right.

Please make sure your video is authored in correct format, otherwise the uploading process will return an error
2

hashtag
Fill in content details

While the video is processing, feel free to fill in content details:

  • Video title

  • Description (optional)

  • Thumbnail image

  • Genre tags

3

hashtag
Preview your content

Once the video is uploaded and processed, it is available for preview!

Click View button to see your video in a new tab. You can adjust Quality Settings on the fly to check how it was transcoded into different resolutions.

4

hashtag
Adjust visibility

The visibility is set to Private by default, but you can always set it to Public or Unlisted in the dropdown at the top of the page

5

hashtag
Save and publish

When you're happy with everything, click Create button at the top right corner — this will save your video with the Visibility Settings you adjusted earlier.

And when you're ready to publish — just click Edit and set your video to Public. Congratulations, it's now live and can be viewed by entire VIVERSE community!

Please note that you can always hide your video later by setting its Visibility back to Private

Video Duration

  • Max: 120 min

Video Bitrate

  • Max: 60 Mbps (H.264)

  • Max: 45 Mbps (H.265)

  • Min: 60 Kbps

Audio Codec

  • AAC

  • AC-3

  • MPEG Audio

Audio Bitrate

  • Max: 576 Kbps

  • Min: 24 Kbps

The sweet spot for quality / speed

1920x1080

H.264 (AVC) 4 - 6 Mbps

AAC-LC 192 kbps / 48kHz

Fallback for compatibility

1280x720

H.264 (AVC) 2 - 3 Mbps

AAC-LC 192 kbps / 48kHz

Mobile optimization

720x480

H.264 (AVC) 1 - 1.5 Mbps

AAC-LC 128 kbps / 48kHz

Data saver mode

640x360

H.264 (AVC) 0.8 - 1.0 Mbps

AAC-LC 128 kbps / 48kHz

Legacy mobile support

400x240

H.264 (AVC) 0.4 - 0.6 Mbps

AAC-LC 128 kbps / 48kHz

Restricted bandwidth

384 kbps (Hi-Fi)

4K

2K @ 60fps 2560x1440

1440p60, 1080p60, 720p60, 480p30, 360p30, 240p30

384 kbps (Hi-Fi)

HD

2K @ 30fps 2560x1440

1440p30, 1080p30, 720p30, 480p30, 360p30, 240p30

384 kbps (Hi-Fi)

HD

1080p @ 60fps 1920x1080

1080p60, 720p60, 480p30, 360p30, 240p30

192 kbps (Stereo)

HD

1080p @ 30fps 1920x1080

1080p30, 720p30, 480p30, 360p30, 240p30

192 kbps (Stereo)

HD

720p @ 60fps 1280x720

720p60, 480p30, 360p30, 240p30

128 kbps (Standard)

HD

720p @ 30fps 1280x720

720p30, 480p30, 360p30, 240p30

128 kbps (Standard)

HD

480p (Any FPS) 720x480

480p30, 360p30, 240p30

128 kbps (Standard)

SD

File Format

  • Audio Video Interleave (.avi)

  • MPEG-1 (.mpg)

  • MPEG-4 (.mp4, .m4v)

  • MPEG transport stream (.ts)

  • MPEG-2 Transport Stream (.ts, .m2ts)

  • QuickTime Movie (.mov)

  • Matroska (.mkv)

  • Windows Media Video (.wmv)

  • Blu-ray Disc Audio-Video (.m2ts)

File Size

  • Max: 30 GB

Video Codec

  • MPEG-1/2/4

  • AVC (H.264)

  • HEVC (H.265)

  • Apple ProRes

  • AV1

Video Resolution

3840x2160

H.265 (HEVC) 15 - 30 Mbps

AAC-LC 384 kbps / 48kHz

High efficiency for VR/Desktop clarity

2560x1440

H.265 (HEVC) 8 - 14 Mbps

4K @ 60fps 3840x2160

2160p60, 1440p60, 1080p60, 720p60, 480p30, 360p30, 240p30

384 kbps (Hi-Fi)

4K

4K @ 30fps 3840x2160

APPLICATIONS
VIDEOS
specifications for applications and 3D experiences
specifications for video content
VIVERSE CLIarrow-up-right
install and authentication
VIVERSE Studioarrow-up-right
From the Upload section, click the light blue "Create New World" button in the top right to open this modal.
Once created, your App ID will display on the world page.
  • Max: 4096x2304

  • Min: 128x128

AAC-LC 384 kbps / 48kHz

2160p30, 1440p30, 1080p30, 720p30, 480p30, 360p30, 240p30

If the user clicks on a blue box before both tasks are completed, the Quest system resets and needs to be triggered again.

hashtag
Create the quest

A. Give the quest a name in the Quest name field. The text: Find the boxes! was added.

B. Give the quest a description in the Quest description field. The text: Click on the boxes that are a specific color. was added.

C. Create the first task and give the task a description in the Task description field. The text: Click on the red box. was added.

D. Set the Task type to check.

E. Create the second task and give the task a description in the Task description field. The text: Click on the green boxes. was added.

F. Set the Task type to progressBar.

G. Add the value 4 to the Progress Steps field.

3

hashtag
Create the trigger area that will start the quest

A. Create a new 3D Box entity.

B. Add a Collision component.

C. Adding a material is optional. A transparent material has been added so that the trigger area is visible in play mode.

D. Click the Edit Viverse Extension button.

4

hashtag
Starting the quest

A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

B. Add a Trigger and select EntitySubscribeTriggerEnter.

C. Add local-player to the tags to filter field.

D. Add an Action and select Quest.

E. In the selected quest field, choose Find the boxes!

F. In the quest response field, choose startQuest.

5

hashtag
Create an object that can be clicked to complete the first task

A. Create a 3D object.

B. Add a Collision component.

C. Add a material. Red has been added because this will be the box that will be clicked on to complete the first task.

D. Click the Edit Viverse Extension button.

6

hashtag
Completing the first task

A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

B. Add a Trigger and select NotificationCenterSubscribeEntityPicking.

C. Add an Action and select Quest.

D. In the selected quest field, choose Find the boxes!

E. In the quest response field, choose completeTask.

F. In the selected task field, choose Click on the red box.

7

hashtag
Create multiple objects that can be clicked to complete the second task

A. Create multiple 3D objects.

B. Add a Collision component to each object.

C. Add a material. Green has been added because these will be the boxes that will be clicked on to complete the second task.

D. Click the Edit Viverse Extension button.

8

hashtag
Completing the second task

The following steps are completed for all green boxes.

A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

B. Add a Trigger and select NotificationCenterSubscribeEntityPicking.

C. Add an Action and select Quest.

D. In the selected quest field, choose Find the boxes!

E. In the quest response field, choose addTaskProgress.

F. In the selected task field, choose Click on the green boxes.

9

hashtag
Create multiple objects that can be clicked to reset the quest

A. Create multiple 3D objects.

B. Add a Collision component to each object.

C. Add a material. Blue has been added because these will be the boxes that will be clicked on to reset the quest.

D. Click the Edit Viverse Extension button.

10

hashtag
Resetting the quest

The following steps are completed for all blue boxes.

A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

B. Add a Trigger and select NotificationCenterSubscribeEntityPicking.

C. Add an Action and select Quest.

D. In the selected quest field, choose Find the boxes!

E. In the quest response field, choose resetQuest.

The avatar enters the trigger area and the Quest system dialog appears.

With the Quest system started, the user clicks on the red box and the first task is completed.

When the user clicks each green box, progress is added to the second task.

Once the user clicks on the final green box, the second task is complete and the Quest system dialog disappears.

C. Adding a material is optional. A transparent material has been added so that the trigger area is visible in play mode.

D. Click the Edit Viverse Extension button.

2

hashtag
Add the EntityCheckPoint action

A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

B. Add a Trigger and select EntitySubscribeTriggerEnter.

C. Add local-player to the tags to filter field.

D. Add an Action and select EntityCheckPoint.

E. Add an entity that has a position that will be used for the new spawn location. SpawnLocation1 has been added to the pick up an entity id.

C. Adding a material is optional. A transparent material has been added so that the trigger area is visible in play mode.

D. Click the Edit Viverse Extension button.

2

hashtag
Add the TeleportAvatar action

A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

B. Add a Trigger and select EntitySubscribeTriggerEnter.

C. Add local-player to the tags to filter field.

D. Add an Action and select EntityTeleportAvatar.

E. Add an entity that has a position that will be used for the teleport location. TeleportLocation2 has been added to the Specify the Entity whose location you want to teleport to.

The avatar enters the green trigger area and the trigger area becomes the new spawn location.

The avatar has fallen off the map and needs to be respawned.

When the avatar respawns, it is spawned at the new location.

The avatar enters the green trigger area.

After entering the green trigger area, the avatar is teleported to another location.

2

hashtag
Add the Seat module

A. In the VIVERSE extension, select the Seat plugin for the Select plugins dropdown.

B. Select the Seat module and add it.

C. Add a value to the number of seats field.

3

hashtag
Adjust the sitting boundary

A. The Seat is automatically generated.

B. Adjust the Radius on the Collision component to modify the sitting boundary.

4

hashtag
View the SeatHintFarAway icon

A. The SeatHintFarAway icon is a white dot that is automatically generated and hovers above each seat.

5

hashtag
Adjust the boundary for the SeatHintFarAway icon

A. The SeatHintFarAwayTriggerSphere is automatically generated.

B. Adjust the Radius on the Collision component to modify the distance away before the SeatHintFarAway icon displays. Also ensure the height of the collider is above ground level, so the player capsule collider is sure to intersect with it.

6

hashtag
View the SeatHint button

A. The SeatHint button has an icon of a sitting person. The button is automatically generated and hovers above each seat. When clicked, the avatar will sit.

7

hashtag
Adjust the boundary for the SeatHint button

A. The SeatHintTriggerSphere is automatically generated.

B. Adjust the Radius on the Collision component to modify the distance away before the SeatHint icon displays.

When the avatar enters the SeatHintFarAwayTriggerSphere, the SeatHintFarAway icon (white dot) shows above the seat.

When the avatar enters the SeatHintTriggerSphere, the SeatHint button (sitting person icon) shows above the seat.

When the avatar clicks on the SeatHint button, the avatar sits down.

Entity Physics

This document provides a guide that can be used to add a physics force to objects in VIVERSE project. The action can be configured to execute when a trigger is activated.


hashtag
EntityRigidbodyAddForceInPhysics

Create An Action That Adds Force To An Object

This guide provides instructions for setting up the EntityRigidbodyAddForceInPhysics action. In the sample app, once the avatar enters the blue trigger area, a force is applied to the sphere.

In this example, a trigger is created and when the avatar or other objects enters the trigger area, an action adds force to an object. Any object can be used as a trigger, as long as the object has a collision component. This example uses a 3D box as the trigger area.

1

hashtag
Create a trigger area

A. Create a new 3D Box entity.

B. This Collision component is not required for the EntityRigidbodyAddForceInPhysics action to work. The Collision component is required for the EntitySubscribeTriggerEnter trigger that will be used in this example.

Edit Mode

This page details the basics of Edit Mode for decorating your world.


hashtag
Getting Started with Edit Mode

Edit Mode is available on all worlds you own or where you have been added as a co-owner/moderator. If you are a world owner or co-owner, you will have access to the "Edit Mode" button when you join the world.

The Edit Mode button

When enabling Edit Mode, your controls and interface will be changed, and your camera will be detached from your avatar so that you can more easily navigate and edit your world. When in Edit Mode, your avatar will still be visible and your audio will be audible to others in the world.

The Edit Mode interface has three sections, the performance report in the top-left corner, the media upload toolbar in the bottom of your screen, and the object list in the top right where you can access and control the settings of all media added to your world in Edit Mode.

In order to save your customizations and have others see them, you will need to select the "Close edit mode" button and exit Edit Mode.

hashtag
Viewing the Performance Report

1

hashtag
Open the Performance requirement link

Step 1. Click Performance requirement (A).

2

hashtag
Adding and Handling Media

Media can be added from several different sources through the toolbar in the bottom of the Edit Mode interface: Marketplace assets, uploaded assets, and assets linked from external sources. For more information on the types of media you can add and their settings, see .

When media are added to your world, you can access and update their properties through the object list on the right-hand side of the Edit Mode interface. Furthermore, you can quickly reposition your media by clicking and dragging them through your world.

hashtag
Adding Collaborators

You can add five additional VIVERSE accounts as co-owners of your world. By promoting these users, you are giving them full control over Edit Mode, moderation tools, and world settings for your world. Co-owners can be removed and will not be able to delete the world on your behalf. Co-owners can be added outside of Edit Mode by going to the hamburger settings button in the top-right of your world, selecting the "Permissions" tab, and adding Co-Owners by their user id or display name.

Images

This document provides a guide that can be used to setup images and extend the functionality of images in a VIVERSE project.


hashtag
Media Images

hashtag
Adding Images And Extending Image Functionality

An image has been added to the scene and the image has been configured to always face the avatar

1

hashtag
Setup image

A. Create a new Image Element.

B. Add the image to the Texture field.

C. Resize the image using the Width and

Intro to Standalone App Publishing

This page overviews the requirements, tools, and process for building applications on VIVERSE.


hashtag
Publishing Apps to VIVERSE

3D experiences and web applications are the foundation of VIVERSE's creator community. We invest heavily in the development of amazing interactive content and are always expanding our support for new tools and frameworks where the best experiences are being crafted for the web.

While VIVERSE supports custom publishing pipelines for a few platforms, any platform can publish to VIVERSE using by uploading a .zip file with an index.html file at the top level of its file tree.

Asset Management

This document provides several guides that can be used to optimize VIVERSE projects by controlling when assets are rendered.


hashtag
EntityAssetUnload

Create An Action That Unloads An Asset From The Scene, Stops The Asset From Rendering And Retains The Asset In Memory

This guide provides instructions for setting up the EntityAssetUnload action. In the sample app, the trigger area is outlined in blue. Once the avatar enters into the trigger area, the asset stops rendering in the scene. This will decrease draw calls and improve frames per second, but the asset will be retained in memory.

Custom Virtual Reality UX

Use `XrService` to interact with virtual reality devices and controllers.


WebXR experiences can run on desktop, mobile and virtual reality devices alike. Utilizing the XrService in the Create SDK, you can write custom code to manage VR controllers, locomotion settings, and XR session callbacks.

hashtag
Custom Locomotion Settings

By default, the VIVERSE character controller uses teleport for locomotion, since this is the more comfortable option, in general. However, smooth locomotion (where the player glides smoothly across the floor) can be enabled on either or both controllers by setting the

npm install -g @viverse/cli
viverse-cli auth login
viverse-cli auth login -e <email> -p <password>
viverse-cli auth login -e $VVS_EMAIL -p $VVS_PASSWORD
viverse-cli auth status
viverse-cli auth logout
viverse-cli app create
viverse-cli app create --name <application-name>
viverse-cli app list
viverse-cli app publish <path> --app-id <your-app-id>
viverse-cli app publish --app-id <your-app-id>
magnetometer - Allows access to magnetometer sensor data
  • midi - Allows access to MIDI devices

  • window-management - Allows multi-window management

  • xr-spatial-tracking - Allows access to VR/AR features

  • MP3
    C. Adding a material is optional. A transparent material has been added so that the trigger area is visible in play mode.

    D. Click the Edit Viverse Extension button.

    2

    hashtag
    Add the EntitySubscribeTriggerEnter trigger

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeTriggerEnter.

    C. Add local-player to the tags to filter field.

    D. Add an Action and select NotificationCenterPublish.

    E. Create a unique name for the notification and add it to the notification name to publish field. In this example, the AddForce name is added.

    3

    Create the 3D object that the physics force will be applied to

    A. Create a new 3D object.

    B. Add a Collision component.

    C. Add a Rigidbody component.

    D. Set the Collision Type to Dynamic.

    E. Click the Edit Viverse Extension button.

    4

    hashtag
    Add the EntityRigidbodyAddForceInPhysics action

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select NotificationCenterSubscribe.

    C. The same text that was added to the notification name to publish needs to be added to the notification name to subscribe.

    D. Add an Action and select EntityRigidbodyAddForceInPhysics.

    E. In the X Force, Y Force and Z Force fields, add values for the amount of force to apply in each direction.

    The sphere is not moving before the avatar enters the trigger area.

    Once the avatar enters the trigger area, a force is applied to the sphere in the direction based on the parameters.

    hashtag
    View the world's VRAM consumption

    Step 1. View the Performance Manager report (B).

    this page
    The Edit Mode controls
    The Edit Mode interface.
    The media upload toolbar
    The Edit Mode object list
    Adding Co-owners through world permissions.
    Height
    .

    D. Click the Edit Viverse Extension button.

    2

    hashtag
    Add the Image module

    A. In the VIVERSE extension, select the Media plugin for the Select plugins dropdown.

    B. Select the Image module and add it.

    C. Place a checkmark by the billboard property to have the image always facing the avatar.

    hashtag
    Supported Tools & Frameworks
    Platform
    Description

    HTML5 & WebGL

    Creators can generally publish any project compatible with HTML5. See for details on requesting advanced features, such as user microphone permissions.

    WebGL & WebGPU

    Creators can generally publish any WebGL project on VIVERSE. Certain features for WebGPU, such as support for multi-threading, may need to be approved manually. See for how to submit requests for these permissions.

    PlayCanvas

    VIVERSE supports publishing from PlayCanvas through and with the , which adds multiplayer avatars, UI, and a cross-device character controller by default.

    ThreeJS

    Don't see your platform or want to request new features? Join our Discord Serverarrow-up-right and let us know if you would like more support.

    hashtag
    Developer Tools

    Along with our platform integrations that allow you to publish to VIVERSE, we also have a number of standalone developer tools that can be utilized in experiences hosted on and off of VIVERSE. These tools help creators get the most out of their 3D experience and make the difficult parts of online development easier.

    NOTE: VIVERSE SDKs cannot be used with projects published via the PlayCanvas Create SDK extensionarrow-up-right, which do not have App IDs.

    Tool
    Description
    Documentation

    Polygon Streaming

    Upload and embed high quality 3D assets in web-based experiences. Polygon Streaming's innovative technology makes highly-detailed, large assets accessible to billions of devices through the web browser!

    Avatar & Account SDK

    Utilize our avatar and virtual identity system to bring multiplayer to your 3D experience.

    hashtag
    Bringing Your Own Game Servers & Analytics

    While VIVERSE provides free services and SDKs for creators to integrate into their projects, developers may also provision and connect with their own servers/analytics on VIVERSE. This process does require manual approval by the VIVERSE team, but we are able to quickly approve these requests. Please email the VIVERSE teamenvelope with your request and a description of the external server you would like to connect to.

    Studio

    When the avatar is outside the blue trigger area, the house asset is being rendered.

    When the avatar enters the blue trigger area, the house asset is unloaded, but is retained in memory.

    In this example, a trigger is created and when triggered, an action unloads an object. Any object can be used as a trigger, as long as the object has a collision component. This example uses a 3D box as the trigger area.

    1

    hashtag
    Create a trigger area

    A. Create a new 3D Box entity.

    B. The Collision component is not required for EntityAssetUnload action to work. The Collision component is required for the EntitySubscribeTriggerEnter trigger that will be used in this example.

    C. Adding a material is optional. A transparent material has been added so that the trigger area is visible in play mode.

    D. Click the Edit Viverse Extension button.

    2

    hashtag
    Add the EntityAssetUnload action

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeTriggerEnter.

    hashtag
    EntityAssetReload

    Create An Action That Reloads An Asset Into The Scene From Memory And Causes The Asset To Be Rendered

    This guide provides instructions for setting up the EntityAssetReload action. In the sample app, the trigger area is outlined in blue. Once the avatar leaves the trigger area, the asset is reloaded from memory and starts rendering in the scene.

    When the avatar enters the blue trigger area, the house asset is unloaded, but is retained in memory.

    When the avatar leaves the blue trigger area, the house asset is reloaded from memory.

    In this example, a trigger is created and when triggered, an action reloads an object. Any object can be used as a trigger, as long as the object has a collision component. This example uses a 3D box as the trigger area.

    1

    hashtag
    Create a trigger area

    A. Create a new 3D Box entity.

    B. The Collision component is not required for EntityAssetReload action to work. The Collision component is required for the EntitySubscribeTriggerLeave trigger that will be used in this example.

    C. Adding a material is optional. A transparent material has been added so that the trigger area is visible in play mode.

    D. Click the Edit Viverse Extension button.

    2

    hashtag
    Add the EntityAssetReload action

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeTriggerLeave.

    hashtag
    EntityDestroy

    Create An Action That Destroys An Asset, Removes It From Memory and Stops It From Rendering

    This guide provides instructions for setting up the EntityDestroy action. In the sample app, the trigger area is outlined in blue. Once the avatar enters the trigger area, the asset is destroyed, which removes it from memory and stops it from rendering.

    When the avatar is outside the blue trigger area, the house asset is being rendered.

    When the avatar enters the blue trigger area, the house asset is destroyed and removed from memory.

    In this example, a trigger is created and when triggered, an action destroys an object. Any object can be used as a trigger, as long as the object has a collision component. This example uses a 3D box as the trigger area.

    1

    hashtag
    Create a trigger area

    A. Create a new 3D Box entity.

    B. The Collision component is not required for EntityDestroy action to work. The Collision component is required for the EntitySubscribeTriggerEnter trigger that will be used in this example.

    C. Adding a material is optional. A transparent material has been added so that the trigger area is visible in play mode.

    D. Click the Edit Viverse Extension button.

    2

    hashtag
    Add the EntityDestroy action

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeTriggerEnter.

    of each.

    Import the XrServicearrow-up-right from the Create SDK into an .mjs script. Per the API docs, this gives you access to both left and right controllersarrow-up-right, and their properties, to set on init:

    NOTE: this script asset must be placed in /scripts or another subfolder, since it assumes the VIVERSE SDK is one level up, located at: "../@viverse/create-sdk.mjs" - or you can alter this import path as needed. For more information on how .mjs scripts and imports work, see Introduction to MJS.

    When switching to Smooth Locomotion, this also enables flight in VR in any World where flight is enabled in World Settings. Just "click" the Smooth Locomotion joystick to enter flight mode. Once flying, pressing forward on the joystick will fly forward along the VR camera's forward axis (i.e. wherever you're looking), and vice versa backwards.

    hashtag
    Custom Controller Models

    Checking the IXrController interface further, we can use the setModelAsset() function to set custom 3D models for our controllersarrow-up-right, instead of the default VIVERSE models.

    After defining the vrControllerAssetL and vrControllerAssetR attributes of type Asset in the above script, we then reference custom 3D controller assets in the editor, which our script instantiates at runtime in VR.

    LocomotionTypearrow-up-right

    Godot HTML5

    This document provides a guide for exporting a Godot project for HTML 5 and publishing to VIVERSE.


    hashtag
    Introduction

    In this getting-started guide, we will cover the basics of setting up a Godot HTML5arrow-up-right project and publishing to VIVERSE using the VIVERSE CLIarrow-up-right.

    circle-info

    For this guide, we are using the VIVERSE CLI, but it is also possible to compress and .

    hashtag
    Project Settings for Godot

    Web support for Godot currently requires the engine version to be Godot 4.1 or higher for successful exports. Currently using Godot without C# is essential. However, web export support for C# is expected soon from a recent .

    circle-exclamation

    Godot 3 web exports are technically supported but not focused on future support.

    hashtag
    Export Setting for Godot

    Select the desired platform for export. For web exporting, choose HTML5. Also make sure that the export path is set to Build/index.html. This will lower your packaged build size and rename project exported .html to index.html so VIVERSE can easily find and run the project.

    hashtag
    Publish to VIVERSE

    From here you can publish the project using the CLI tool just like any other platform.

    1

    hashtag
    Log in to VIVERSE platform

    A. Open a command prompt and type: viverse-cli auth login, then click Enter.

    B. Enter VIVERSE email and password.

    hashtag
    General Limitations of Godot and Web Export

    List of Godot WebGL rendering issues:

    Networked

    This page details the usage of the networked component on individual entities in PlayCanvas.


    hashtag
    Overview

    The Networked plugin allows local updates to certain properties of an entity's components to be sent to other connected avatars.

    how you can earn on VIVERSE
    import { Script, Asset } from "playcanvas";
    import { XrService, XrTypes } from "../@viverse/create-sdk.mjs";
    
    export class ViverseXrManager extends Script {
      static scriptName = "viverseXrManager";
      
      initialize() {
        this.xrService = new XrService();
        this.xrService.controllers.right.locomotionType = XrTypes.LocomotionTypes.Teleport;
        this.xrService.controllers.left.locomotionType = XrTypes.LocomotionTypes.Smooth;  // Smooth
        // See enum definitions here:
        // https://viveportsoftware.github.io/pc-lib/enums/XrTypes.LocomotionTypes.html
      }
    }
    import { Script, Asset } from "playcanvas";
    import { XrService } from "../@viverse/create-sdk.mjs";
    
    export class ViverseXrManager extends Script {
      static scriptName = "viverseXrManager";
      
      /**
      * @attribute
      * @type {Asset}
      */
      vrControllerAssetR = null;
    
      /**
      * @attribute
      * @type {Asset}
      */
      vrControllerAssetL = null;
    
      initialize() {
        this.xrService = new XrService();
        this.xrService.controllers.right.setModelAsset(this.vrControllerAssetR);
        this.xrService.controllers.left.setModelAsset(this.vrControllerAssetL);
      }
    }
    C. Add local-player to the tags to filter field.

    D. Add an Action and select EntityAssetUnload.

    E. Add an object that will be unloaded when the avatar enters the trigger area. The House2 entity has been added to the pick up specify execution entity.

    C. Add local-player to the tags to filter field.

    D. Add an Action and select EntityAssetReload.

    E. Add an object that will be unloaded when the avatar enters the trigger area. The House2 entity has been added to the pick up specify execution entity.

    C. Add local-player to the tags to filter field.

    D. Add an Action and select EntityDestroy.

    E. Add an object that will be destroyed when the avatar enters the trigger area. The House2 entity has been added to the pick up specify execution entity.

    C. Confirm login was successful.
    2

    hashtag
    Publish Content

    A. To publish content to VIVERSE type the following command with the project path to the project's production build folder: viverse-cli publish <path>, then click Enter.

    B. Enter an Application title and Application description.

    C. Confirm the content was published successfully.

    3

    hashtag
    Re-publishing content

    A. To re-publish content to VIVERSE when a project is already published, type the following command with the project path to the project's production build folder: viverse-cli publish <path>, then click Enter.

    B. Confirm the manifest file is updated.

    C. Confirm the content was published successfully.

    4

    hashtag
    Test project

    A. Confirm project was published successfully and working properly in VIVERSE by visiting the URL that is printed in the Publish Details.

    upload your build file directly to the VIVERSE Studio
    announcementarrow-up-right
    https://github.com/godotengine/godot/issues/66458arrow-up-right
    hashtag
    Transform

    By adding the Transform component, an entity's position and rotation will be networked across clients.

    circle-exclamation

    At this time, Transform does not network the Scale property of the entity.

    hashtag
    Anim

    By adding the Anim component, an entity's animation state will be networked across clients.

    hashtag
    Networking Example

    In this video, we have created an arena with a floor, four walls and a ball. The networking module has been added to the ball. After publishing and creating the world in VIVERSE, multiple players can join in the environment. The location of the ball is tracked across all player sessions.

    To create the arena and ball in the video, you can follow the Create Your First PlayCanvas Project tutorial. The instructions below can be used to add Networking functionality to the ball or any other entity of your choosing.

    1

    hashtag
    Create the entity

    A. We have already created the Ball entity in the tutorial linked above. Here's a screenshot of the Ball entity.

    2

    hashtag
    Add the Networking module

    A. In the VIVERSE extension, select the Networking plugin for the Select plugins dropdown.

    B. Change the dropdown to Transform in the Select a module and add field. Click the plus sign.

    C. Confirm the enabled checkbox is checked.

    3

    hashtag
    Publish and create world

    A. To test the project, publish to VIVERSE and create the world.

    4

    hashtag
    Share and play

    A. Share the world link with another player and have them join the environment. We the other player interacts with the ball, you should be able to see the ball movement in your session. When you move the ball, the other player should be able to see the ball movement in their session.

    VIVERSE supports most ThreeJS projects, as well as the many tools and frameworks that have been built on top of the library, such as React-Three-Fibre (R3F)nd AFRAME. VIVERSE sponsors the development of an open source ThreeJS and R3F-compatible avatar system called pmndrs/viversearrow-up-right.

    Unity WebGL

    Creators can publish projects built with Unity's WebGL export pipeline, and we recommend they upgrade to the latest version of Unity to benefit from the recent upgrades for web optimization made in Unity 6. We have written multiple examples in for how to utilize our SDKs in your Unity Projects.

    Open Brush

    In December 2025, VIVERSE worked with the Icosa Foundation, to build a pipeline for publishing from Open Brush to VIVERSE with multiplayer avatars.

    Wonderland Engine

    In November 2025, VIVERSE worked with the Wonderland Engine team to develop a plug-in for Wonderland Enginearrow-up-right, including examples of a cross-device character controller and one-click publishing to VIVERSE.

    Godot HTML5

    VIVERSE supports publishing from Godot using their HTML5 publishing pipeline.

    BabylonJS

    VIVERSE supports publishing Babylon projects.

    Unreal Engine

    Unfortunately, there is no 1st-party pipeline for publishing from Unreal Engine to web. VIVERSE is open to discussing support for any 3rd party publishing/streaming pipelines from Unreal Engine to the web.

    Leaderboard SDK

    Add a leaderboard to single and multiplayer gaming experiences. Our leaderboard system allows you to keep track of player performance between sessions.

    Matchmaking & Networking SDK

    Add persistent data to single and multiplayer gaming experiences.

    Storage SDK

    Persist data between sessions for users associated with their VIVERSE account.

    How to Publish
    VIVERSE Studio
    VIVERSE Toolkit
    See here
    Bringing Your Own Game Servers & Analytics

    ThreeJS

    This document provides a guide for creating a sample app in Three.js, building the app with Vite and deploying the app to VIVERSE.


    hashtag
    Introduction

    In this getting-started guide, we will cover the basics of setting up a ThreeJS project and publishing to VIVERSE using the VIVERSE CLIarrow-up-right.

    circle-info

    For this guide, we are using the VIVERSE CLI, but it is also possible to compress and .

    hashtag
    Installing Node.js

    1

    hashtag
    Download Node.js

    A. Download the latest version of Node.js (LTS) from http .

    2

    hashtag
    Installing Three.js and making an example project

    This guide is a walkthrough for creating an example Three.js project

    1

    hashtag
    Create project folder

    A. Create a new folder that will contain the project.

    2

    hashtag
    Installing Vite and using it to build your project

    1

    hashtag
    Install the build tool Vite

    A. If choosing to use Vite as the build tool, it needs to be installed in the Three.js project folder also. With command prompt opened and the directory set to your Three.js project, type the command: npm install --save-dev vite.

    hashtag
    Installing the VIVERSE CLI

    1

    hashtag
    Install the VIVERSE (CLI) command-line tool

    A. Inside a command prompt, type: npm install -g @viverse/cli, then click Enter. Installing a package with -g installs the package globally. The location of globally installed packages depends on your operating system and npm configuration:

    hashtag
    Logging in with the VIVERSE CLI

    1

    hashtag
    Login to VIVERSE platform

    A. Open a command prompt and type: viverse-cli auth login, then click Enter.

    B. Enter VIVERSE email and password.

    hashtag
    Publishing from VIVERSE

    1

    hashtag
    Publish content

    A. To publish content to VIVERSE type the following command with the project path to the project's production build folder: viverse-cli publish <path>, then click Enter.

    B. Enter an Application title and Application description.

    PlayCanvas Toolkit Changelog

    Get download link to the latest version of VIVERSE Extension and explore the changelog.


    circle-check

    hashtag
    VIVERSE PlayCanvas Extension v3.58.3

    Release Date
    Version
    Release Notes

    Entity Collision Enabling & Disabling

    This document provides several guides that can be used to enable and disable colliders in a VIVERSE project. These actions can be configured to execute when triggers are activated.


    hashtag
    EntityToggleCollision

    This guide provides instructions for setting up the EntityToggleCollision action. In the sample app, every time the avatar enters the green trigger area, a notification is sent to the wall and the collider on the wall is toggled on/off. The wall is green when the avatar can pass through and red when the avatar can not pass through.

    In this example, a trigger is created and when the avatar enters the trigger area, an action sends a notification to toggle another object's collider on/off. Any object can be used as a trigger, as long as the object has a collision component. This example uses a 3D box as the trigger area.

    hashtag
    An action that toggles an object's collider on/off

    1

    Create the 3D object that will send the notification

    A. Add a 3D object to the scene.

    B. The Collision component is not required for EntityToggleCollision action to work. The Collision component is required for the EntitySubscribeTriggerEnter trigger that will be used in this example.

    C. Click the Edit Viverse Extension button.

    2

    hashtag
    EntityEnableCollision

    Create An Action That Enables An Object's Collider

    This guide provides instructions for setting up the EntityEnableCollision action. In the sample app, every time the avatar enters the red trigger area, a notification is sent to the wall object and the collider on the wall object is enabled. The wall is green when the avatar can pass through and red when the avatar can not pass through.

    In this example, a trigger is created and when the avatar enters the trigger area, an action sends a notification to enable another object's collider. Any object can be used as a trigger, as long as the object has a collision component.

    1

    hashtag
    Create a trigger area

    A. Create a new 3D Box entity.

    B. Add a Collision component.

    C. Adding a material is optional. A transparent material has been added so that the trigger area is visible in play mode.

    hashtag
    EntityDisableCollision

    Create An Action That Disables An Object's Collider

    This guide provides instructions for setting up the EntityDisableCollision action. In the sample app, every time the avatar enters the green trigger area, a notification is sent to the wall object and the collider on the wall object is disabled. The wall is green when the avatar can pass through and red when the avatar can not pass through.

    In this example, a trigger is created and when the avatar enters the trigger area, an action sends a notification to disable another object's collider. Any object can be used as a trigger, as long as the object has a collision component.

    1

    hashtag
    Create a trigger area

    A. Create a new 3D Box entity.

    B. Add a Collision component.

    C. Adding a material is optional. A transparent material has been added so that the trigger area is visible in play mode.

    hashtag
    Automatically install the necessary tools

    A. Use the defaults during the installation, but place a checkmark in the Automatically install the necessary tools checkbox

    3

    hashtag
    Confirm Node.js is installed with at least v22

    A. Open a command prompt and type: node, then click Enter.

    B. Confirm that Node.js is installed when the following message prints in command prompt: Welcome to Node.js v##.##.##.

    hashtag
    Create the index.html

    A. Create the index.html page inside the project folder. This can be done by creating a text file, pasting the code and saving it as a .HTML page or using an IDE, such as Visual Studio Code.

    index.html

    3

    hashtag
    Create the main.js

    A. Create the main.js page inside the project folder. This can be done by creating a text file, pasting the code and saving it as a .JS file or using an IDE, such as Visual Studio Code.

    main.js

    4

    hashtag
    Install Three.js framework

    A. Three.js needs to be installed in the project folder. Open command prompt and change directories to your Three.js project.

    B. Type: npm install --save three.

    5

    hashtag
    Confirm Three.js framework is installed

    A. Confirm node_modules folder and package.json have been added to the directory.

    B. Confirm the three folder folder and .package-lock.json have been added to the node_modules directory.

    2

    hashtag
    Confirm Vite has been installed

    A. Confirm Vite has been installed by checking for additional folders in the node_modules folder.

    3

    hashtag
    Add the Vite build settings

    A. Add the Vite build settings to package.json file.

    package.json

    4

    hashtag
    Add the vite.config.js file

    A. Add the vite.config.js file to the root of the project.

    vite.config.js

    5

    hashtag
    Create a development build of the Three.js project

    A. To create a development build of the Three.js project, type the following command inside command prompt under the Three.js project directory: npx vite.

    B. Confirm the development build of the Three.js project was built successfully when Vite provides a localhost URL to test.

    6

    hashtag
    Test development build

    A. To test a development build of the Three.js project, open the browser and navigate to the URL that was printed in the previous step. In this example, the URL is http://localhost:5173arrow-up-right. Confirm the app works as expected.

    7

    hashtag
    Create production build

    A. To create a production build of the Three.js project, type the following command inside command prompt under the Three.js project directory: npx vite build.

    B. Confirm the production build of the Three.js project was built successfully by confirming the dist folder was created and populated.

    Windows : In windows, packages are installed in %APPDATA%\npm\node_modules.

  • macOS and Linux : In mac or Linux packages are typically installed in /usr/local/lib/node_modules or a user-specific directory like ~/.npm-global.

  • B. Confirm that the command line tool is installed based on screen feedback.

    C. Confirm login was successful.
    C. Confirm the content was published successfully.
    2

    hashtag
    Re-publishing content

    A. To re-publish content to VIVERSE when a project is already published, type the following command with the project path to the project's production build folder: viverse-cli publish <path>, then click Enter.

    B. Confirm the manifest file is updated.

    C. Confirm the content was published successfully.

    3

    hashtag
    Test project

    A. Confirm project was published successfully and working properly in VIVERSE by visiting the URL that is printed in the Publish Details.

    upload your build file directly to the VIVERSE Studio
    https://nodejs.org/enarrow-up-right

    • Quest Celebration Event Bug: Fix the issue where quests with celebrations configured are not triggered as completed after the user finishes the task.

    • Sync the Trigger Fix: Fix the issue where "Sync the trigger" in Trigger and Action is not working.

    7/18/2025

    • Flying in VR: switch to Smooth Locomotion using the XRService, then in any World where flying is enabled in World Settings, press down/click the Smooth Locomotion joystick to enter flight mode. Once flying, pressing forward on the joystick will fly forward along the VR camera's forward axis (i.e. wherever you're looking), and vice versa backwards.

    • The Enter VR button has been made more reliable.

    7/7/2025

    • Fixes most cases where the no-code extension was losing being lost when the PlayCanvas editor disconnects & reconnects to necessary backend services.

      • Also now displays a warning modal if the extension does enter a disconnected state.

    • XRService.start() now accepts a callback so code execution can be paused prior to successful VR entry.

    6/23/2025

    • XRService now includes new locomotion options you can set per controller: smooth, teleport, and none

      • As a minor optimization, we now hide the VR cursor when its inputSource is lost

    5/23/2025

    • Bug fix: prevent duplicate injection of custom loading screen scripts during publishing.

    • Re-enables debugging in the browser by adding publishing modes: Debug mode is designed to assist in development and debugging processes, whereas Standard mode delivers an optimized, minified build intended for distribution.

    4/29/2025

    Add SDK support to functions

    -Methods: start

    -Methods: end

    4/16/2025

    • Add 'Enable Flying' option in VIVERSE scene settings

    • Fixed turnToward method

    4/9/2025

    Add SDK support to functions

    -Methods: addCheckTask

    -Methods: addProgressBarTask

    -Methods: fire

    -Methods: getTaskById

    -Methods: off

    3/27/2025

    Add SDK support to functions

    -Methods: resetToViverseAvatar

    -Properties: avatar

    3/12/2025

    Add SDK support to functions

    -Properties: handedness

    -Properties: inputSource

    -Properties: modelEntity

    -Methods: resetModelAsset

    -Methods: setModelAsset

    2/26/2025

    • Adding switchPov methods to change 1st & 3rd person POV through code

    2/6/2025

    • Add 3 features in Trigger & Action - EntityAssetUnload, EntityAssetReload, EntityAssetDestroy

    1/7/2025

    • Add post effects settings

    • Add scene ownership check on publish tab

    12/3/2024

    • Handle response err message from PlayCanvas server job api

    • Fix service worker error

    12/27/2024

    • Error handling when publishing the scene

    10/21/2024

    • Support latest Chrome version

    12/2/2025

    3.58.2arrow-up-right

    • Fix the issue where adding or removing a post effect doesn’t immediately update the UI

    • Update Polygon Streaming SDK to 2.6.8

    8/15/2025

    3.57.1arrow-up-right

    • Adds support for publishing PlayCanvas projects that utilize multiple scenes, as well as an IWorldNavigationService to the Create SDK API to allow for programmatic scene switching.

    • Adds the ability to set nearClip and farClip camera configuration:

    DOWNLOADarrow-up-right

    8/5/2025

    hashtag
    Add the NotificationCenterPublish action

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeTriggerEnter.

    C. Add local-player to the tags to filter field.

    D. Add an Action and select NotificationCenterPublish

    E. Create a unique notification name and add it to the notification name to publish field. In this example, the notification is called ToggleWall.

    3

    hashtag
    Create the 3D object that will receive the notification

    A. Add a 3D object to the scene.

    B. Add a Collision component.

    C. Add a Rigidbody component.

    D. Click the Edit Viverse Extension button.

    4

    hashtag
    Add the EntityToggleCollision action

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select NotificationCenterSubscribe.

    C. The same text that was added to the notification name to publish needs to be added to the notification name to subscribe.

    D. Add an Action and select EntityToggleCollision.

    D. Click the Edit Viverse Extension button.

    2

    hashtag
    Add the EntitySubscribeTriggerEnter trigger

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeTriggerEnter.

    C. Add local-player to the tags to filter field.

    E. Add an Action and select NotificationCenterPublish.

    F. Create a unique notification name and add it to the notification name to publish field. In this example, the notification is called EnableWall.

    3

    hashtag
    Create the object that will have it's collider enabled

    A. Create a new 3D Box entity.

    B. Add a Collision component.

    C. Adding a material is optional. A transparent material has been added so that the trigger area is visible in play mode.

    D. Click the Edit Viverse Extension button.

    4

    hashtag
    Add the EntityEnableCollision action

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select NotificationCenterSusbscribe.

    C. The same text that was added to the notification name to publish needs to be added to the notification name to subscribe.

    D. Add an Action and select EntityEnableCollision.

    D. Click the Edit Viverse Extension button.

    2

    hashtag
    Add the EntitySubscribeTriggerEnter trigger

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeTriggerEnter.

    C. Add local-player to the tags to filter field.

    E. Add an Action and select NotificationCenterPublish.

    F. Create a unique notification name and add it to the notification name to publish field. In this example, the notification is called DisableWall.

    3

    hashtag
    Create the object that will have it's collider disabled

    A. Create a new 3D Box entity.

    B. Add a Collision component.

    C. Adding a material is optional. A transparent material has been added so that the trigger area is visible in play mode.

    D. Click the Edit Viverse Extension button.

    4

    hashtag
    Add the EntityDisableCollision action

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select NotificationCenterSubscribe.

    C. The same text that was added to the notification name to publish needs to be added to the notification name to subscribe.

    D. Add an Action and select EntityDisableCollision.

    Before the avatar enters the green trigger area, the wall is red and the avatar can not pass through it.

    Once the avatar enters the green trigger area, the collider on the wall is toggled to be disabled. The wall is green and the avatar can pass through it.

    The avatar is able to pass through the wall when the wall’s collider is disabled.

    Once the avatar enters the red trigger area, the EntityEnableCollision action enables the wall’s collider

    The avatar is unable to pass through the wall once the wall’s collider is enabled.

    The avatar is unable to pass through the wall when the wall’s collider is enabled.

    Once the avatar enters the green trigger area, the action disables the wall’s collider

    The avatar is able to pass through the wall when the wall’s collider is disabled.

    Connecting No-Code Events to Custom Scripts

    This document provides a guide for setting up no-code events in custom scripts.


    The VIVERSE no-code events can be accessed through custom scripts. The events can be found in the root directory of your PlayCanvas project in the @viverse/create-extensions-sdk.mjs file. Here's a look at the file.

    hashtag
    Trigger usage

    hashtag
    Calling custom code from script when the player clicks on an object

    Let's say we want to give our players the ability to click on an object and when that object is clicked, we want to call a method from a custom script. This is a good use-case for the NotificationCenterSubscribeEntityPicking trigger. In this example, when the user clicks on the specific object, a door opens.

    1

    hashtag
    Add the NotificationCenterSubscribeEntityPicking trigger

    A. Select the object that the player will click on and add the NotificationCenterSubscribeEntityPicking trigger using the PlayCanvas extension. No need to add an action.

    2

    hashtag
    Tigger usage with more flexibility

    hashtag
    Calling custom code from script when the player clicks on an object and making the script more flexible

    In the previous example, we created a custom script called ClickableObject.mjs. The script added functionality that rotates a door whenever a specific object is clicked. Let's say there was another scenario where we wanted to have a 2nd object that the user could click on, but we wanted to fire a different event other than the rotateDoor method. We could easily copy our ClickableObject.mjs script, give it a different name and change the rotateDoor call inside the script. The downside here is that we now have 2 scripts that we have to maintain. As alternative, we can remove the rotateDoor event call, add an attribute to our script for the event name and then add the different event names through the PlayCanvas editor.

    1

    hashtag
    Update the ClickableObject.mjs script

    A. Modify the code in ClickableObject.mjs script to the following.

    2

    hashtag
    Additional trigger usage

    Calling custom code from script when a player enters a trigger area

    Another common scenario that creators may face is firing an event when a player enters a trigger area. This is a good use-case for the EntitySubscribeTriggerEnter trigger. In this example, when the user enters a trigger area, a door opens.

    1

    hashtag
    Add the EntitySubscribeTriggerEnter trigger

    A. Select the trigger area that the player will walk through and add the EntitySubscribeTriggerEnter trigger using the PlayCanvas extension. No need to add an action.

    2

    hashtag
    Action usage

    hashtag
    Calling custom code from script when an action executed

    Let's say we want to give our players the ability to click on an object and when that object is clicked, we want to call a method from a custom script. This is a good use-case for the NotificationCenterSubscribeEntityPicking trigger. In this example, when the user clicks on the specific object, a door opens.

    1

    hashtag
    Add the EntitySubscribeTriggerEnter trigger and the EntityDisable action

    A .Select the trigger area that the player will walk through and add the EntitySubscribeTriggerEnter trigger along with the EntityDisable action using the PlayCanvas extension.

    2

    Animation & Sound

    This document provides several guides that can be used to setup event listeners for animations, control animations and control audio in a VIVERSE project.

    hashtag
    EntityPlayAnimation

    Create An Action That Plays An Animation

    This guide provides instructions for setting up the EntityPlayAnimation action. In the sample app, once the avatar enters the trigger area, the character switches to the dancing animation state. When the avatar leaves the trigger area, the character switches back to the idle animation state.

    Toolkit Setup

    Learn how to install the VIVERSE Chrome Extension for PlayCanvas Editor, create your first world, then publish it to VIVERSE.


    hashtag
    Introduction

    PlayCanvas is an open-source game engine designed to advance the development of 3D web games, interactive content and rich multimedia. Unlike similar WebGL engines like Three.js or Babylon, PlayCanvas comes with sophisticated in-browser Editor (inspired by early Unity 2.x - 3.x), which provides its users with a rich set of tools — allowing to assemble scenes from imported assets, setup real-time and baked lighting, write custom scripts, create animation graphs, and much much more.

    To make creator experience even smoother, VIVERSE has developed a special Extension for Chrome browsers which extends PlayCanvas Editor functionality with additional set of no-code tools and publishing options. It provides common building blocks like Triggers, Actions and Quests, enables Player locomotion and Avatar system in your project, and allows one-click testing and publishing right from the Editor.

    Sample Project

    A PlayCanvas sample project that provides demonstrations of how to use the features in the VIVERSE Extension.


    We created a PlayCanvas sample project that contains examples of how to utilize the PlayCanvas Extension. Download the project and import it into a PlayCanvas account. Publish the project to VIVERSE and enter Preview mode to test out the functionality. The pages listed under API Reference are the functionality that the sample project is demoing.

    Project Name
    Download
    Version
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8">
        <title>My first three.js app</title>
        <style>
          body { margin: 0; }
        </style>
      </head>
      <body>
        <script type="module" src="/main.js"></script>
      </body>
    </html>
    import * as THREE from 'three';
    
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
    
    const renderer = new THREE.WebGLRenderer();
    renderer.setSize( window.innerWidth, window.innerHeight );
    renderer.setAnimationLoop( animate );
    document.body.appendChild( renderer.domElement );
    
    const geometry = new THREE.BoxGeometry( 1, 1, 1 );
    const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
    const cube = new THREE.Mesh( geometry, material );
    scene.add( cube );
    
    camera.position.z = 5;
    
    function animate() {
    
      cube.rotation.x += 0.01;
      cube.rotation.y += 0.01;
    
      renderer.render( scene, camera );
    
    }
    {
      "scripts": {
        "dev": "vite",
        "build": "vite build"
      },
      "dependencies": {
        "three": "^0.175.0"
      },
      "devDependencies": {
        "vite": "^6.3.2"
      }
    }
    
    import { defineConfig } from 'vite';
    import path from 'path';
    
    // https://vite.dev/config/
    export default defineConfig({
      base: './', // Use relative path as base URL
    });
     
    export const TriggerTypes = {
      Base: 'trigger:0',
      Echo: 'trigger:1',
      PCAppEventSubscribe: 'trigger:100001',
      NotificationCenterSubscribe: 'trigger:200001',
      NotificationCenterSubscribeEntityPicking: 'trigger:200002',
      TheatreJSSubscribe: 'trigger:210001',
      TheatreJSSubscribeSheetEnd: 'trigger:210002',
      EntitySubscribeAnimationEvent: 'trigger:300001',
      EntitySubscribeAnimationStart: 'trigger:300002',
      EntitySubscribeAnimationEnd: 'trigger:300003',
      EntitySubscribeCollisionStart: 'trigger:300004',
      EntitySubscribeCollisionEnd: 'trigger:300005',
      EntitySubscribeTriggerEnter: 'trigger:300006',
      EntitySubscribeTriggerLeave: 'trigger:300007',
      SitInSeat: 'trigger:300008',
      SharePhoto: 'trigger:400001',
      EnterAnyWorld: 'trigger:400002',
      EnterMyWorld: 'trigger:400003'
    }
    
    export const ActionTypes = {
      Base: 'action:0',
      Echo: 'action:1',
      PCAppEventPublish: 'action:100001',
      NotificationCenterPublish: 'action:200001',
      TheatreJSPublish: 'action:210001',
      TheatreJSPlaySheet: 'action:210002',
      EntityRigidbodyAddForceInPhysics: 'action:300001',
      EntityPlayAnimation: 'action:300002',
      EntityEnable: 'action:300003',
      EntityDisable: 'action:300004',
      EntityToggleEnabled: 'action:300005',
      EntityFadeIn: 'action:300006',
      EntityFadeOut: 'action:300007',
      EntityPlaySound: 'action:300008',
      EntityEnableCollision: 'action:300009',
      EntityDisableCollision: 'action:300010',
      EntityToggleCollision: 'action:300011',
      InworldNpcWaitingSpeak: 'action:300012',
      InworldNpcStopWaitingSpeak: 'action:300013',
      EntityEnableByTag: 'action:300014',
      EntityDisableByTag: 'action:300015',
      EntityCheckPoint: 'action:300016',
      EntityEnableById: 'action:300017',
      EntityDisableById: 'action:300018',
      EntityStopSound: 'action:3000019',
      EntityAssetUnload: 'action:3000020',
      EntityAssetReload: 'action:3000021',
      EntityDestroy: 'action:3000022',
      ParticleSystemPlay: 'action:3000023',
      Quest: 'action:400001',
      TeleportAvatar: 'action:400002',
      AssignUserAsset: 'action:400003',
      Vote: 'action:400004',
      ShowToastMessage: 'action:400005',
      TaskComplete: 'action:400006'
    }
    optimizing your work for the web

    hashtag
    Create the ClickableObject.mjs script

    A. Create the following script and name it ClickableObject.mjs.

    B. Add ClickableObject.mjs script to the object that the player will click on.

    3

    hashtag
    Create the DoorRotator.mjs script

    A. Create the following script and name it DoorRotator.mjs.

    B. Add the DoorRotator.mjs script to an object or entity.

    4

    hashtag
    Confirm the event is being fired

    A. Test the functionality by publishing to VIVERSE and clicking on the object that has the ClickableObject.mjs script added to it.

    B. Confirm the event was fired by checking in the browser debug console.

    hashtag
    Add the event name in to the script inside the PlayCanvas editor

    A. Add the DoorRotator:rotateDoor text to the Event To Fire attribute text field.

    B. If we added ClickableObject.mjs to a 2nd object and wanted to fire a different method, it could look like the following screenshot.

    3

    hashtag
    Confirm the event is being fired

    A. Test the functionality by publishing to VIVERSE and clicking on the object that has the ClickableObject.mjs script added to it.

    B. Confirm the event was fired by checking in the browser debug console. It should yield the same results as in the previous example.

    hashtag
    Create the TriggerArea.mjs script

    A. Create the following script and name it TriggerArea.mjs.

    B. Add TriggerArea.mjs script to trigger area that the player will walk through.

    3

    hashtag
    Confirm the event is being fired

    A. Test the functionality by publishing to VIVERSE and walking through the trigger area that has the TriggerArea.mjs script added to it.

    B. Confirm the event was fired by checking in the browser debug console. It should yield the following results.

    hashtag
    Create the DisabledObject.mjs script

    A. Create the following script and name it DisableObject.mjs.

    B. Add DisabledObject.mjs script to trigger area that the player will walk through. In this example the DisabledObject.mjs script was added to the trigger area in the previous example. Be sure to disable or remove the TriggerArea.mjs script from the previous example.

    3

    hashtag
    Confirm the event is being fired

    A. Test the functionality by publishing to VIVERSE and walking through the trigger area that has the DisabledObject.mjs script added to it.

    B. Confirm the event was fired by checking in the browser debug console. It should yield the following results.

    1st- and 3rd-person camera FOVs now settable in PlayCanvas:
    Re-enable debugging in the browser

    -Align game_script.js behavior with esm.js to allow setting breakpoints

    • Enable custom loading screen

    -Methods: on

    -Methods: reset

    -Methods: start

    • IQuestServicearrow-up-right

    -Methods: addQuest

    -Methods: fire

    -Methods: getQuestById

    -Methods: getQuestByName

    -Methods: off

    -Methods: on

    -Methods: resetAllQuests

    • ITaskarrow-up-right

    -Methods: fire

    -Methods: off

    -Methods: on

    • IProgressBarTaskarrow-up-right

    -Methods: addProgress

    -Methods: fire

    -Methods: off

    -Methods: on

    -Properties: network

    -Properties: nametag

    -Properties: profile

    • IPlayerServicearrow-up-right

    -Properties: localPlayer

    -Properties: remotePlayers

    -Properties: playerCount Add enable/disable toggle of microphone and microphone permission function in “Player Config > Disable Microphone”

  • IXrControllerEventsarrow-up-right

  • -Properties: add

    -Properties: remove

    • IXrServicearrow-up-right

    -Properties: controllers

    • IXrServiceEventsarrow-up-right

    -Properties: controller:addInput

    -Properties: controller:removeInput

    • CameraServicearrow-up-right

    -Properties: canRotate

    -Properties: canZoom

    -ILocalPlayer

    -Methods: scaleAvatar

    3.55.0arrow-up-right
    3.52.0arrow-up-right
    3.51.0arrow-up-right
    3.50.1arrow-up-right
    3.49.0arrow-up-right
    3.48.0arrow-up-right
    IXrServicearrow-up-right
    3.46.1arrow-up-right
    3.45.3arrow-up-right
    IQuestarrow-up-right
    3.45.0arrow-up-right
    ILocalPlayerarrow-up-right
    IPlayerarrow-up-right
    3.44.12arrow-up-right
    IXrControllerarrow-up-right
    3.44.7arrow-up-right
    3.43.1arrow-up-right
    3.40.7arrow-up-right
    3.38.0arrow-up-right
    3.36.4arrow-up-right
    3.35.19arrow-up-right

    The character is in the idle state when the avatar is outside of the trigger area.

    The character switches to the dancing animation state when the avatar enters the trigger area.

    In this example, a trigger area is created and when triggered, an action plays an animation. Any object can be used as a trigger, as long as the object has a collision component.

    1

    hashtag
    Create the animation state graph

    A. Create an Animation State Graph and rename the initial state to Idle.

    B. Create an additional animation state and name it Dancing.

    2

    hashtag
    Configure 3D model for animation

    A. Add a 3D model to the scene.

    B. Add an Anim component.

    C. Add the Animation State Graph.

    D. Add the Idle animation.

    3

    hashtag
    Create a trigger area

    A. Create a new 3D Box entity.

    B. The Collision component is not required for EntityPlayAnimation to work. The Collision component is required for the EntitySubscribeTriggerEnter trigger that will be used in this example.

    4

    hashtag
    Add the EntityPlayAnimation action for the dancing animation state

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeTriggerEnter.

    5

    hashtag
    Add the EntityPlayAnimation action for the Idle animation state

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeTriggerLeave.

    hashtag
    EntitySubscribeAnimationStart

    Create A Trigger Based On When An Animation Starts

    This guide provides instructions for setting up the EntitySubscribeAnimationStart trigger. In the sample app, when the avatar enters the blue trigger area, the character’s dancing animation begins to play. The dancing animation has an animation event added and the animation event is triggered whenever the animation starts to play.

    Before the character’s dancing animation begins.

    When the character’s dancing animation begins, the animation event is triggered and text is displayed to show that the animation event was triggered.

    In the EntityPlayAnimation example, a 3D model was added, an animation state graph was created, animation states were created and a trigger area was created to initiate the animations. In this example, an animation event is created for the start of the animation.

    1

    hashtag
    Open animation file that will have the animation event

    A. Click on the animation file to open up the properties.

    2

    hashtag
    Create animation event

    A. Create an event, add value 0 to the time field and start to the name field.

    3

    hashtag
    Add the EntitySubscribeAnimationStart trigger

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeAnimationStart.

    hashtag
    EntitySubscribeAnimationEnd

    Create A Trigger Based On When An Animation Ends

    This guide provides instructions for setting up the EntitySubscribeAnimationEnd trigger. In the sample app, when the avatar enters the blue trigger area, the character’s dancing animation begins to play. The dancing animation has an animation event added and the animation event is triggered whenever the animation stops playing.

    The character’s dancing animation has started.

    When the character’s dancing animation ends, the animation event is triggered and text is displayed to show that the animation event was triggered.

    In the EntityPlayAnimation example, a 3D model was added, an animation state graph was created, animation states were created and a trigger area was created to initiate the animations. In this example, an animation event is created for the end of the animation.

    1

    hashtag
    Open animation file that will have the animation event

    A. Click on the animation file to open up the properties.

    2

    hashtag
    Create animation event

    A. Create an event, add value 8 to the time field and end to the name field. The value 8 is used because the dancing animation has a duration of 8.83 seconds.

    3

    hashtag
    Add the EntitySubscribeAnimationEnd trigger

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeAnimationEnd.

    hashtag
    EntitySubscribeAnimationEvent

    Create A Trigger Based On A Specific Time During An Animation

    This guide provides instructions for setting up the EntitySubscribeAnimationEvent trigger. In the sample app, when the avatar enters the blue trigger area, the character’s dancing animation begins to play. The dancing animation has an animation event added and the animation event is triggered during the animation.

    The character’s dancing animation has started.

    During the character’s dancing animation, the animation event is triggered and text is displayed to show that the animation event was triggered.

    In the EntityPlayAnimation example, a 3D model was added, an animation state graph was created, animation states were created and a trigger area was created to initiate the animations. In this example, an animation event is created for during the animation.

    1

    hashtag
    Open animation file that will have the animation event

    A. Click on the animation file to open up the properties.

    2

    hashtag
    Create animation event

    A. Create an event, add value 4 to the time field and create a unique name for the event. In this example, the event name animate was added to the name field.

    3

    hashtag
    Add the EntitySubscribeAnimationEvent trigger

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeAnimationEvent.

    hashtag
    EntityPlaySound

    Create An Action That Plays A Sound

    This guide provides instructions for setting up the EntityPlaySound action. In the sample app, once the avatar enters the trigger area, a sound plays.

    The avatar is outside of the blue trigger area and the sound does not play.

    The avatar enters the blue trigger area and the sound plays.

    In this example, a trigger is created and when triggered, an action plays a sound. Any object can be used as a trigger, as long as the object has a collision component. This example uses a 3D box as the trigger area.

    1

    hashtag
    Create a trigger area

    A. Create a new 3D Box entity.

    B. The Collision component is not required for EntityPlaySound action to work. The Collision component is required for the EntitySubscribeTriggerEnter trigger that will be used in this example.

    C. Adding a material is optional. A transparent material has been added so that the trigger area is visible in play mode.

    D. Add a Sound component.

    E. Create a unique name for the sound file and add it to the name field. In this example, Slot2 is added.

    F. Add the audio file to the Asset field.

    G. Click the Edit Viverse Extension button.

    2

    hashtag
    Add the EntityPlaySound action

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeTriggerEnter.

    hashtag
    EntityStopSound

    Create An Action That Stops Playing A Sound

    This guide provides instructions for setting up the EntityStopSound action. In the sample app, once the avatar leaves the trigger area, the sound stops playing.

    The avatar enters the blue trigger area and the sound plays.

    The avatar is outside of the blue trigger area and the sound stops playing.

    In this example, a trigger is created and when triggered, an action plays a sound. Any object can be used as a trigger, as long as the object has a collision component. This example uses a 3D box as the trigger area.

    1

    hashtag
    Create a trigger area

    A. Create a new 3D Box entity.

    B. The Collision component is not required for EntityStopSound action to work. The Collision component is required for the EntitySubscribeTriggerLeave trigger that will be used in this example.

    C. Adding a material is optional. A transparent material has been added so that the trigger area is visible in play mode.

    D. Add a Sound component.

    E. Create a unique name for the sound file and add it to the name field. In this example, Slot2 is added.

    F. Add the audio file to the Asset field.

    G. Click the Edit Viverse Extension button.

    2

    hashtag
    Add the EntityStopSound action

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeTriggerLeave.

    Below is a useful set of tutorials to help you get started with VIVERSE PlayCanvas Extension. We assume you're already familiar with PlayCanvas Editor itself and have PlayCanvas account. If not, please feel free to explore PlayCanvas User Manualarrow-up-right first!

    hashtag
    Install PlayCanvas Extension

    1

    hashtag
    Get the latest Extension from VIVERSE

    • the latest version of Playcanvas Extension

    • Unzip downloaded file on your computer

    2

    hashtag
    Navigate to Chrome Extensions manager

    • Click Extensions icon in Chrome toolbar and open the Extensions Popup

    3

    hashtag
    Install the Extension

    • Enable Developer Mode at the top right corner

    hashtag
    Create and publish your first project

    circle-info

    Before proceeding any further, we assume you're familiar with the basics of PlayCanvas Editor. If you're looking for a comprehensive introduction to it — please refer to a dedicated PlayCanvas Editor Manualarrow-up-right first!

    1

    hashtag
    Initialize your new project

    • Create a new PlayCanvas project or open an already existing one

    • Make sure VIVERSE Extension is properly initialized — you should see ExtensionEntity in your scene hierarchy and @viverse folder added to your project. Refresh the page if it's not the case

    • Login into VIVERSE via dedicated button in the left toolbar. Once logged in, you should see VIVERSE Scene Settings button there

    2

    hashtag
    Setup 3D environment

    • First, delete default Camera in Scene Hierarchy. The Extension will provide its own Camera system that will be added to your Scene at runtime

    3

    hashtag
    Create a Spawn Point

    • In order for a Player to appear in your world, it should be spawned somewhere first. VIVERSE Extension provides a convenient way to control that!

    4

    hashtag
    Preview your project

    • Once you have a walkable floor and a spawn point, it's time to test your project live!

    5

    hashtag
    Final: Create a VIVERSE World for your project

    • After confirming everything is working correctly in preview mode, you can publish your project to production environment!

    hashtag
    "Factory Reset" PlayCanvas Extension

    Sometimes issues in the installation process can produce persistent bugs that are only fixed by fully resetting the Extension. If you're experiencing these bugs please follow the steps below

    1

    hashtag
    Sign in and out of VIVERSE

    Sign out of your account on viverse.com and then sign back in. MAKE SURE you are using the same email account as the one associated with your PlayCanvas account

    2

    hashtag
    Delete VIVERSE entities from PlayCanvas project

    Go back to your PlayCanvas project and delete:

    • The Extension Entity from your scene hierarchy

    3

    hashtag
    Clear Application Storage

    Open the in your Chrome brower, go to "Application", select "Storage" in the left hand toolbar and click the "Delete Site Data" button

    4

    hashtag
    Refresh the page

    Refresh your PlayCanvas project page with the VIVERSE extension enabled

    5

    hashtag
    Sign in back to PlayCanvas

    Sign in to PlayCanvas when prompted. MAKE SURE you are using the same email account as the one associated with your VIVERSE account

    6

    hashtag
    Sign in back to VIVERSE

    Sign in to VIVERSE from within your PlayCanvas project using the VIVERSE Scene Settings

    This project is separated into different stations. Each station has Tags added to help identify the VIVERSE functionality the station is demonstrating.

    Name
    Station

    1

    1

    1

    VIVERSE_PlayCanvas_Sample_1.2

    Zip Filearrow-up-right

    Sample Project Version: 1.2

    PlayCanvas Extension Version: 3.44.1

    Event Listeners

    This page provides several guides that can be used to setup event listeners in a VIVERSE project. The event listeners use triggers. Triggers can be configured to perform actions once activated.


    hashtag
    EntitySubscribeTriggerEnter

    This guide provides instructions for setting up the EntitySubscribeTriggerEnter trigger. In the sample app below, the trigger areas are outlined in blue. Once the avatar enters into the blue area, an action occurs.

    import { Script } from 'playcanvas';
    import { TriggerTypes } from '../@viverse/create-extensions-sdk.mjs';
    
    export class ClickableObject extends Script {
        /**
         * Called when the script is about to run for the first time.
         */
        initialize() {
            const event = TriggerTypes.NotificationCenterSubscribeEntityPicking;
            this.entity.on(event, () => {
                this.objectClicked();
                this.app.fire('DoorRotator:rotateDoor');
            });
        }
    
        /**
         * Called for enabled (running state) scripts on each tick.
         * 
         * @param {number} dt - The delta time in seconds since the last frame.
         */
        update(dt) {
        }
    
        objectClicked() {
            console.log('Objected Clicked!');
        }
    }
    import { Script } from 'playcanvas';
    
    export class DoorRotator extends Script {
        /**
         * Called when the script is about to run for the first time.
         */
        initialize() {
            // listen for the DoorRotate:rotateDoor event
            this.app.on('DoorRotator:rotateDoor', this.rotateDoor);
        }
    
        /**
         * Called for enabled (running state) scripts on each tick.
         * 
         * @param {number} dt - The delta time in seconds since the last frame.
         */
        update(dt) {
        }
    
        rotateDoor() {
            console.log('Rotate Door!');
        }
    }
    import { Script } from 'playcanvas';
    import { TriggerTypes } from '../@viverse/create-extensions-sdk.mjs';
    
    export class TriggerArea extends Script {
        /**
         * Called when the script is about to run for the first time.
         */
        initialize() {
            const event = TriggerTypes.EntitySubscribeTriggerEnter;
            this.entity.on(event, () => {
                this.triggerEntered();
                this.app.fire('DoorRotator:rotateDoor');
            });
        }
    
        /**
         * Called for enabled (running state) scripts on each tick.
         * 
         * @param {number} dt - The delta time in seconds since the last frame.
         */
        update(dt) {
        }
    
        triggerEntered() {
            console.log('Trigger Entered!');
        }
    }
    import { Script } from 'playcanvas';
    import { ActionTypes } from '../@viverse/create-extensions-sdk.mjs';
    
    export class DisabledObject extends Script {
        /**
         * Called when the script is about to run for the first time.
         */
        initialize() {
            const event = ActionTypes.EntityDisable;
            this.entity.on(event, () => {
                this.objectDisabled();
                this.app.fire('DoorRotator:rotateDoor');
            });
        }
    
        /**
         * Called for enabled (running state) scripts on each tick.
         * 
         * @param {number} dt - The delta time in seconds since the last frame.
         */
        update(dt) {
        }
    
        objectDisabled() {
            console.log('Object Disabled!');
        }
    }
    import { Script } from 'playcanvas';
    import { TriggerTypes } from '../@viverse/create-extensions-sdk.mjs';
    
    export class ClickableObject extends Script {
        /**
         * @attribute
         * eventToFire
         * @type { string }
         * @title Event To Fire
         */
        eventToFire
    
        /**
         * Called when the script is about to run for the first time.
         */
        initialize() {
            const event = TriggerTypes.NotificationCenterSubscribeEntityPicking;
            this.entity.on(event, () => {
                this.objectClicked();
                this.app.fire(this.eventToFire);
            });
        }
    
        /**
         * Called for enabled (running state) scripts on each tick.
         * 
         * @param {number} dt - The delta time in seconds since the last frame.
         */
        update(dt) {
        }
    
        objectClicked() {
            console.log('Objected Clicked!');
        }
    }

    1

    EntityEnablyByTagarrow-up-right

    2

    EntityDisableByTagarrow-up-right

    2

    EntitySubscribeCollisionStartarrow-up-right

    3

    EntityDisablearrow-up-right

    3

    EntitySubscribeCollisionEndarrow-up-right

    4

    EntityFadeOutarrow-up-right

    4

    EntityFadeInarrow-up-right

    4

    EntityEnableCollisionarrow-up-right

    5

    EntityDisableCollisionarrow-up-right

    5

    NotificationCenterPublisharrow-up-right

    6

    NotificationCenterSubscribearrow-up-right

    6

    EntityToggleCollisionarrow-up-right

    6

    EntityPlaySoundarrow-up-right

    7

    EntityStopSoundarrow-up-right

    7

    TeleportAvatararrow-up-right

    8

    EntityPlayAnimationarrow-up-right

    9

    NotificationCenterSubscribeEntityPickingarrow-up-right

    10

    Seatarrow-up-right

    11

    EntityRigidbodyAddForceInPhysicsarrow-up-right

    12

    EntityToggleEnabledarrow-up-right

    13

    EntityCheckPointarrow-up-right

    14

    EntitySubscribeAnimationStartarrow-up-right

    15

    EntitySubscribeAnimationEndarrow-up-right

    15

    EntitySubscribeAnimationEventarrow-up-right

    15

    Questarrow-up-right

    16

    Imagearrow-up-right

    17

    Videoarrow-up-right

    18

    Audioarrow-up-right

    19

    Polygon Streamingarrow-up-right

    20

    EntityAssetUnloadarrow-up-right

    21

    EntityAssetReloadarrow-up-right

    21

    EntityDestroyarrow-up-right

    22

    EntitySubscribeTriggerEnterarrow-up-right
    EntitySubscribeTriggerLeavearrow-up-right
    EntityEnableByIdarrow-up-right
    EntityDisableByIdarrow-up-right
    E. Add the Dancing animation.
    C. Adding a material is optional. A transparent material has been added so that the trigger area is visible in play mode.

    D. Click the Edit Viverse Extension button.

    C. Add local-player to the tags to filter field.

    D. Add an Action and select EntityPlayAnimation.

    E. Add the name of an animation state to the animate state to play field. In this example, the animation state name is Dancing.

    F. Add the 3d model to the pick up specify execution entity field.

    C. Add local-player to the tags to filter field.

    D. Add an Action and select EntityPlayAnimation.

    E. Add the name of an animation state to the animate state to play field. In this example, the animation state name is Idle.

    F. Add the 3d model to the pick up specify execution entity field.

    C. Add an Action and select EntityEnableById.

    D. For the EntityEnableById action to work, an object needs to be added that will be enabled. In this example, the AnimationStartText object is added.

    C. Add an Action and select EntityEnableById.

    D. For the EntityEnableById action to work, an object needs to be added that will be enabled. In this example, the AnimationEndText object is added.

    C. Add the event name to the event name to subscribe.

    D. Add an Action and select EntityEnableById.

    E. For the EntityEnableById action to work, an object needs to be added that will be enabled. In this example, the AnimationEventText object is added.

    C. Add local-player to the tags to filter field.

    D. Add an Action and select EntityPlaySound.

    E. In the sound name to play field, add the same name that was created on the Sound component.

    F. Add the entity with the Sound component to the pick up specify execution entity.

    C. Add local-player to the tags to filter field.

    D. Add an Action and select EntityStopSound.

    E. In the sound name to play field, add the same name that was created on the Sound component.

    F. Add the entity with the Sound component to the pick up specify execution entity.

    Click Manage Extensions at the bottom and open the Extensions Manager in a new tab

    Click Load Unpacked and select the folder with unpacked Extension you downloaded previously

  • Verify the Extension is now present in Extensions Manager tab

  • Import Ammo physics library and add a Floor plane with Collision component and static Rigidbody. This is crucial because VIVERSE Player system relies on built-in PlayCanvas physics engine for moving around. Without a floor, your player will be simply falling through

  • Add additional geometry and lights if want!

  • Simply create a new empty Entity in your scene and add spawn-point tag to it

  • Make sure it's positioned at the floor level or above. Feel free to rotate it to change the initial orientation of your Player

  • Go to the Publish / Download menu and open the Builds popup window. In that window, click Publish to Viverse button and wait until your project is successfully uploaded

  • Once it's uploaded and ready, the Preview button should appear. Clicking it will open your project in a new tab, in preview mode. This preview URL is public and you can share it with your friends and colleagues to test the current version of your work

  • Return back to PlayCanvas Editor and modify your project as you see fit. Keep publishing and previewing to see your most recent changes live in VIVERSE environment

  • In preview mode, click "Create World" button, give your World a name and click the "Create" button to publish it to the VIVERSE Create platform

  • The resulting URL is the public link to your World. You can return back to your project any time and republish an updated version as you see fit

  • Feel free to configure your World Settings as well

  • The @viverse folder from your project assets

  • The extension-image folder from your project assets

  • The extension-script folder from your project assets

  • Download
    Developer Toolsarrow-up-right

    The avatar is outside the blue trigger area and the gold coin is not visible.

    The avatar has entered the blue trigger area and the gold coin is visible.

    In our example above, a trigger area is created. When the avatar enters the trigger area, an action is then initiated. Any object can be used as a trigger, as long as the object has a collision component. Our example uses a 3D box as the trigger area.

    hashtag
    Create a trigger for when an object enters an area

    1

    Create a trigger area

    A. Create a new 3D Box entity.

    B. Add a Collision component.

    C. Adding a material is optional. A transparent material has been added so that the trigger area is visible in play mode.

    D. Click the Edit Viverse Extension button.

    2

    Add the EntitySubscribeTriggerEnter trigger

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeTriggerEnter.

    C. Add local-player to the tags to filter field.

    D. Add an Action and select EntityEnableById.

    E. Add an object that will be enabled when the avatar enters the trigger area. The golden_coin entity has been added to the pick up specify execution entity. The

    hashtag
    EntitySubscribeTriggerLeave

    This guide provides instructions for setting up the EntitySubscribeTriggerLeave trigger. In the sample app, the trigger areas are outlined in blue. Once the avatar leaves the blue area, an action occurs.

    The avatar is inside the the blue trigger area and the gold coin is visible.

    The avatar leaves the blue trigger area and the gold coin is no longer visible.

    In this example, a trigger area is created and when the avatar leaves the trigger area, an action is initiated. Any object can be used as a trigger, as long as the object has a collision component. This example uses a 3D box as the trigger area.

    hashtag
    Create a trigger for when an object leaves an area

    1

    Create a trigger area

    A. Create a new 3D Box entity.

    B. Add a Collision component.

    C. Adding a material is optional. A transparent material has been added so that the trigger area is visible in play mode.

    D. Adding a material is optional. A transparent material has been added so that the trigger area is visible in play mode.

    2

    Add the EntitySubscribeTriggerLeave trigger

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeTriggerLeave.

    C. Add local-player to the tags to filter field.

    D. Add an Action and select EntityDisableById.

    E. Add an object that will be disabled when the avatar leaves the trigger area. The golden_coin entity has been added to the pick up specify execution entity.

    hashtag
    EntitySubscribeCollisionStart

    This guide provides instructions for setting up the EntitySubscribeCollisionStart trigger. In the sample app, the golden_coin is a trigger. Once the avatar collides with the golden_coin, an action occurs.

    The avatar approaches the gold coin.

    Once the avatar collides with the gold coin, the gold coin is removed.

    In this example, a trigger is created and when the avatar collides with the trigger, an action is initiated. Any object can be used as a trigger, as long as the object has a collision component. Because this specific example uses a 3D object that the avatar can collide with, a RigidBody component needs to be added. In this example, the object golden_coin is used, but a simple cube will suffice.

    hashtag
    Create a trigger for when an object collides with another object

    1

    Create the 3D object that the avatar will collide with

    A. Add 3D object to the scene.

    B. Add a Collision component.

    C. Add a Rigidbody component.

    D. Click the Edit Viverse Extension button.

    2

    Add the EntitySubscribeCollisionStart trigger

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeCollisionStart.

    C. Add local-player to the tags to filter field.

    D. Add an Action and select EntityDisable.

    hashtag
    EntitySubscribeCollisionEnd

    This guide provides instructions for setting up the EntitySubscribeCollisionEnd trigger. In the sample app, a flattened 3D cube is a trigger. Once the avatar stops colliding with the cube, an action occurs.

    The avatar is colliding with the red platform.

    Once the avatar stops colliding with the red platform, the red platform begins to fade out.

    In this example, a trigger is created and when the avatar or other objects collide with the trigger, an action is initiated. Any object can be used as a trigger, as long as the object has a collision component. Because this specific example uses a 3D object that the avatar can collide with, a RigidBody component needs to be added.

    hashtag
    Create A Trigger Based On When An Object Stops Colliding With Another Object

    1

    Create the 3D object that the avatar will collide with

    A. Add 3D object to the scene.

    B. Add a Collision component.

    C. Add a Rigidbody component.

    D. Click the Edit Viverse Extension button.

    2

    Add the EntitySubscribeCollisionEnd trigger

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeCollisionEnd.

    C. Add local-player to the tags to filter field.

    D. Add an Action and select EntityFadeOut.

    hashtag
    NotificationCenterSubscribeEntityPicking

    This guide provides instructions for setting up the NotificationCenterSubscribeEntityPicking trigger. In the sample app, the user clicks on the buttons to show and hide the whale.

    The whale is visible.

    When the user clicks on the whale, the whale is disabled.

    In this example, a trigger area is created on a button. When the user clicks on the trigger area, an action causes the 3D model to be disabled. Any object can be used as a trigger, as long as the object has a collision component.

    hashtag
    Create a trigger for when a user clicks an object

    1

    Create the 3D object that the user will click on

    A. Add a 3D object to the scene. In this example, a button is used, but any 3D object will work.

    B. Add a Collision component.

    C. Click the Edit Viverse Extension button.

    2

    Add the NotificationCenterSubscribeEntityPicking trigger

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select NotificationCenterSubscribeEntityPicking.

    C. Add an Action and select EntityDisable. Add an object that will be disabled.

    hashtag
    NotificationCenterPublish & NotificationCenterSubscribe

    This guide provides instructions for setting up the NotificationCenterPublish action and the NotificationCenterSubscribe trigger. In the sample app, every time the avatar enters the green trigger area, a notification is sent to the wall and the collider on the wall is toggled on/off. The wall is green when the avatar can pass through and red when the avatar can not pass through.

    Before the avatar enters the green trigger area, the wall is red and the avatar can not pass through the wall.

    Once the avatar enters the green trigger area, the green trigger sends a notification to the wall and the collider on the wall is disabled.

    In this example, a trigger is created and when the avatar enters the trigger area, an action sends a notification to another object. The other object receives the notification via trigger, then initiates an action.

    hashtag
    Action and Trigger combination to send a notification from one object to another

    1

    Create the 3D object that will send the notification

    A. Add a 3D object to the scene.

    B. The Collision component is not required for NotificationCenterPublish action or NotificationCenterSubscribe trigger to work. The Collision component is required for the EntitySubscribeTriggerEnter trigger that will be used in this example.

    C. Click the Edit Viverse Extension button.

    2

    Add the NotificationCenterPublish action

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeTriggerEnter.

    C. Add local-player to the tags to filter field.

    D. Add an Action and select NotificationCenterPublish.

    E. Create a unique notification name and add it to the notification name to publish field. In this example, the notification is called ToggleWall.

    3

    Create the 3D object that will receive the notification

    A. Add a 3D object to the scene.

    B. The Collision component is not required for NotificationCenterPublish action or NotificationCenterSubscribe trigger to work. The Collision component is required for the EntityToggleCollision action that will be used in this example.

    C. The Rigidbody component is not required for the NotificationCenterPublish action or NotificationCenterSubscribe trigger to work. The Rigidbody component is required for the EntityToggleCollision action that will be used in this example.

    4

    Add the NotificationCenterSubscribe action

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select NotificationCenterSubscribe.

    C. The same text that was added to the notification name to publish needs to be added to the notification name to subscribe.

    D. Add an Action and select

    Entity Enabling & Disabling

    This document provides several guides that can be used to show and hide objects in a VIVERSE project. These actions can be configured to execute when triggers are activated.


    hashtag
    EntityDisable

    This guide provides instructions for setting up the EntityDisable action. In the sample app, once the avatar collides with the gold coin, the gold coin is disabled.

    In this example, a trigger is created and when triggered, an action disables an object.

    hashtag
    Create an action that disables an object

    1

    Create the 3D Object that will be disabled

    A. Add 3D object to the scene.

    B. The Collision component is not required for EntityDisable action to work. The Collision component is required for the EntitySubscribeCollisionStart trigger that will be used in this example.

    C. The Rigidbody component is not required for the EntityDisable action to work. The Rigidbody component is required for the EntitySubscribeCollisionStart trigger that will be used in this example.

    hashtag
    EntityEnableById

    This guide provides instructions for setting up the EntityEnableById action. In the sample app, once the avatar enters the trigger area, the gold coin is enabled.

    In this example, a trigger is created and when triggered, an action enables an object. Any object can be used as a trigger, as long as the object has a collision component. This example uses a 3D box as the trigger area.

    hashtag
    Create an action that enables a single object

    1

    Create a trigger area

    A. Create a new 3D Box entity.

    B. The Collision component is not required for EntityDisableById action to work. The Collision component is required for the EntitySubscribeTriggerEnter trigger that will be used in this example.

    C. Adding a material is optional. A transparent material has been added so that the trigger area is visible in play mode.

    D. Click the Edit Viverse Extension button.

    hashtag
    EntityDisableById

    This guide provides instructions for setting up the EntityDisableById action. In the sample app, once the avatar leaves the trigger area, the gold coin is disabled.

    In this example, a trigger is created and when triggered, an action disables an object. Any object can be used as a trigger, as long as the object has a collision component. This example uses a 3D box as the trigger area.

    hashtag
    Create an action that disables a single object

    1

    Create a trigger area

    A. Create a new 3D Box entity.

    B. The Collision component is not required for EntityDisableById action to work. The Collision component is required for the EntitySubscribeTriggerLeave trigger that will be used in this example.

    C. Adding a material is optional. A transparent material has been added so that the trigger area is visible in play mode.

    2

    hashtag
    EntityEnableByTag

    This guide provides instructions for setting up the EntityEnableByTag action. In the sample app, once the avatar enters the trigger area, multiple balls are enabled.

    In this example, a trigger is created and when triggered, an action enables multiple objects. Any object can be used as a trigger, as long as the object has a collision component. This example uses a 3D box as the trigger area.

    hashtag
    Create an action that enables multiple objects

    1

    Create a trigger area

    A. Create a new 3D Box entity.

    B. The Collision component is not required for EntityEnableByTag action to work. The Collision component is required for the EntitySubscribeTriggerEnter trigger that will be used in this example.

    C. Adding a material is optional. A transparent material has been added so that the trigger area is visible in play mode.

    D. Click the Edit Viverse Extension button.

    hashtag
    EntityDisableByTag

    This guide provides instructions for setting up the EntityDisableByTag action. In the sample app, once the avatar leaves the trigger area, multiple balls are disabled.

    In this example, a trigger is created and when triggered, an action disables multiple objects. Any object can be used as a trigger, as long as the object has a collision component. This example uses a 3D box as the trigger area.

    hashtag
    Create an action that disables multiple objects

    1

    Create a trigger area

    A. Create a new 3D Box entity.

    B. The Collision component is not required for EntityDisableByTag action to work. The Collision component is required for the EntitySubscribeTriggerLeave trigger that will be used in this example.

    C. Adding a material is optional. A transparent material has been added so that the trigger area is visible in play mode.

    Click the Edit Viverse Extension button.

    hashtag
    EntityFadeIn

    This guide provides instructions for setting up the EntityFadeIn action. In the sample app, once the avatar starts colliding with the red platform, the red platform will fade in.

    In this example, a trigger is created and when triggered, an action fades in the object it has been added to. Any object can be used as a trigger, as long as the object has a collision component. This example uses a 3D box as the trigger area.

    hashtag
    Create an action that fades in an object

    1

    Create the 3D object that will fade in

    A. Add a 3D object to the scene.

    B. A Collision component is not required for the EntityFadeIn action to work. The Collision component is required for the EntitySubscribeCollisionStart trigger that will be used in this example.

    C. A Rigidbody component is not required for the EntityFadeIn action to work. The Rigidbody component is required for the EntitySubscribeCollisionStart trigger that will be used in this example.

    hashtag
    EntityFadeOut

    This guide provides instructions for setting up the EntityFadeOut action. In the sample app, once the avatar stops colliding with the red platform, the red platform will fade out.

    In this example, a trigger is created and when triggered, an action fades an object. Any object can be used as a trigger, as long as the object has a collision component.

    hashtag
    Create an action that fades out an object

    1

    Create the 3D object that will fade out

    A. Add a 3D object to the scene.

    B. A Collision component is not required for the EntityFadeOut action to work. The Collision component is required for the EntitySubscribeCollisionEnd trigger that will be used in this example.

    C. A Rigidbody component is not required for the EntityFadeOut action to work. The Rigidbody component is required for the EntitySubscribeCollisionEnd trigger that will be used in this example.

    hashtag
    EntityToggleEnabled

    This guide provides instructions for setting up the EntityToggleEnabled action. In the sample app, every time the avatar enters the blue trigger area, a notification is sent to the purple box to toggle it’s enabled state.

    In this example, a trigger is created and when the avatar or other objects enter the trigger area, an action sends a notification to toggle another object's enabled state. Any object can be used as a trigger, as long as the object has a collision component.

    hashtag
    Create an action that toggles an object's enabled state

    1

    Create a trigger area

    A. Create a new 3D Box entity.

    B. A Collision component is not required for the EntityToggleEnabled action to work. The Collision component is required for the EntitySubscribeTriggerEnter trigger that will be used in this example.

    C. Adding a material is optional. A transparent material has been added so that the trigger area is visible in play mode.

    D. Click the Edit Viverse Extension button.

    Introduction to MJS

    This document provides an introduction to MJS (.mjs) also known as Modular JavaScript


    Modular JavaScript is an evolved form of JavaScript in which related functionality is kept in a single file or module and that functionality is exposed when required using import and export functionality.

    hashtag
    CommonJS (NodeJS) example

    Here's a comparison between MJS and CommonJS. The latter requires node.js, and relies on require() statements to import other scripts/modules:

    golden_coin
    entity has also been disabled by default.

    D. Click the Edit Viverse Extension button.

    EntityToggleCollision.
    D. Click the Edit Viverse Extension button.
    2

    Add the EntityDisable action

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeCollisionStart.

    C. Add local-player to the tags to filter field.

    D. Add an Action and select EntityDisable.

    2

    Add the EntityEnableById action

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeTriggerEnter.

    C. Add local-player to the tags to filter field.

    D. Add an Action and select EntityEnableById.

    E. Add an object that will be enabled when the avatar enters the trigger area. The golden_coin entity has been added to the pick up specify execution entity. The golden_coin entity has also been disabled by default.

    Add the EntityDisableById action

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeTriggerLeave.

    C. Add local-player to the tags to filter field.

    D. Add an Action and select EntityDisableById.

    E. Add an object that will be disabled when the avatar leaves the trigger area. The golden_coin entity has been added to the pick up specify execution entity. The golden_coin entity has also been disabled by default.

    2

    Add the EntityEnableByTag action

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeTriggerEnter.

    C. Add local-player to the tags to filter field.

    D. Add an Action and select EntityEnableByTag.

    E. Create a unique Tag and add it to the enable entity with tag field. In this example, the Balls tag is added.

    3

    Add Tag to multiple objects

    A. Multiple objects can be enabled using the EntityEnableByTag action. Create multiple objects in the scene.

    B. In the Tags field, add the same tag from the enable entity with tag field. Do this for all of the objects or parents of multiple objects.

    2

    Add the EntityDisableByTag action

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeTriggerLeave.

    C. Add local-player to the tags to filter field.

    D. Add an Action and select EntityDiableByTag.

    E. Create a unique Tag and add it to the enable entity with tag field. In this example, the Balls tag is added.

    3

    Add EntityEnableByTag to multiple objects

    A. Multiple objects can be enabled using the EntityEnableByTag action. Create multiple objects in the scene.

    B. In the Tags field, add the same tag from the enable entity with tag field to all of the objects or parents of multiple objects.

    D. Click the Edit Viverse Extension button.
    2

    Add the EntityFadeIn action

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeCollisionStart.

    C. Add local-player to the tags to filter field.

    D. Add an Action and select EntityFadeIn. To customize how long before the fade begins or duration of the fade, update delay in ms and duration in ms fields.

    D. Click the Edit Viverse Extension button.
    2

    Add the EntityFadeOut action

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeCollisionEnd.

    C. Add local-player to the tags to filter field.

    D. Add an Action and select EntityFadeOut. To customize how long before the fade begins or duration of the fade, update delay in ms and duration in ms fields.

    2

    Add the NotificationCenterPublish action

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select EntitySubscribeTriggerEnter.

    C. Add local-player to the tags to filter field.

    D. Add an Action and select NotificationCenterPublish.

    E. Create a unique name for the notification and add it to the notification name to publish field. In this example, the ToggleEnabled name is added.

    3

    Create a 3D object that will have it's enabled state toggled

    A. Add a 3D object to the scene.

    B. Click the Edit Viverse Extension button.

    4

    Add the NotificationCenterSubscribe trigger

    A. In the VIVERSE extension, select the TriggerAndAction plugin for the Select plugins dropdown.

    B. Add a Trigger and select NotificationCenterSubscribe.

    C. In the notification name to subscribe field, add the same name from the notification name to publish field.

    D. Add an Action and select EntityToggleEnabled.

    The avatar is not colliding with the gold coin.

    Once the avatar collides with gold coin, the gold coin is disabled.

    When the avatar is outside of the blue trigger area, the gold coin is disabled.

    When the avatar enters the blue trigger area, the gold coin is enabled.

    When the avatar is inside the blue the trigger area, the gold coin is enabled.

    When the avatar leaves the blue trigger area, the gold coin is disabled.

    When the avatar is outside the blue trigger area, the balls are disabled.

    When the avatar enters the blue trigger area, the balls are enabled.

    When the avatar enters the blue trigger area, the balls are enabled.

    When the avatar leaves the blue trigger area, the balls are disabled.

    The red platform has been faded out and is invisible, but the avatar can still collide with it.

    Once the avatar begins colliding with the invisible red platform, the red platform fades in and is now visible.

    The avatar is colliding with the red platform.

    Once the avatar stops colliding with the red platform, the red platform begins to fade out.

    The purple box is enabled/visible before the avatar enters the blue trigger area.

    Once the avatar enters the blue trigger area, a notification is sent to the purple box to toggle it’s enabled stated. The purple box is disabled.

    The avatar leaves the blue trigger area.

    Once the avatar enters the blue trigger area again, a notification is sent to the purple box to toggle it’s enabled stated. The purple box is enabled again.

    MJS, on the other hand, can use import statements like other languages.

    hashtag
    MJS PlayCanvas Script Structure

    Further, MJS is now supported by PlayCanvas and the VIVERSE Create SDK, to make script imports easier and mroe modern. Here's an example of a script called GameManager.mjs (critically, the file extension is .mjs, not just .js as usual) created for PlayCanvas, extending its built-in Script class:

    hashtag
    Accessing a function in a MJS(.mjs) file from regular JavaScript (.js) code example

    DoorRotator.mjs can can be added to a PlayCanvas entity. This allows functions to be executed on that entity such as a door that needs to be opened. After adding additional code inside the rotateDoor function, this function can be called from another script to open the door.

    TriggerArea.js - Script can be added to a trigger area entity. When another entity or the avatar enters the trigger area, the rotateDoor function is called and the door will open.

    hashtag
    Debugging MJS files

    The process for debugging MJS files is simple. You can add the debugger; statement to any of the methods.

    With the project running in Chrome, pressing F12 will bring up the Chrome DevTools window. Refresh the page with the DevTools window open and the project will stop at the debugger statement.

    When testing your project in PlayCanvas Launch mode, the Chrome Dev Tools can also be opened by right-clicking on the screen and selecting Inspect.

    1

    hashtag
    Right-click on the viewing area of the project

    2

    hashtag
    Select Inspect

    Open up Chrome DevTools through browser menu

    1

    hashtag
    Click the Customize and control Google Chrome Button in the browser

    2

    hashtag
    Click More Tools

    3

    hashtag
    Click Developer Tools

    Keyboard shortcut for DevTools window

    Breakpoints can also be set manually by clicking on the line code number in the margin. The line code number will be highlighted in blue when a breakpoint is set.

    hashtag
    MJS file with custom properties and methods example

    GameManager.mjs - GameManager script can be used to control the different game states of the project. This example script has multiple properties of different types added (including arrays), some with default values. In addition to the properties, there are multiple functions added that show how these properties can be utilized and their values are printed to the console.

    hashtag
    Key Examples

    chevron-rightlocalPlayerManager.mjs - Control Local Player Propertiesarrow-up-righthashtag
    chevron-rightcameraServiceManager.mjs - Control Camera Service Propertiesarrow-up-righthashtag

    Wonderland Engine

    Integration example of VIVERSE Avatar SDK with Wonderland Cloud networking for multiplayer VR experiences.


    hashtag
    Introduction

    In November 2025, VIVERSE and the Wonderland Engine team collaborated to develop a plugin allowing developers to publish to VIVERSE from directly within the Wonderland Engine editor. In addition, we developed examples of using the VIVERSE Avatar and Account SDKs with Wonderland's multiplayer SDK.

    This document is a copy of the Wonderland Engine document covering the plugin: https://github.com/WonderlandEngine/viverse-example/tree/mainarrow-up-right

    hashtag
    Overview

    This repository is a template project that demonstrates how to integrate the VIVERSE Avatar SDK with Wonderland Cloud networking. It provides a compact, production-minded workflow to:

    • Spawn and persist user data (name, avatar) via the VIVERSE Avatar SDK

    • Relay avatar + presence state to Wonderland Cloud for performant, medium-scale multiplayer sessions

    • Use spatial audio for immersive multiplayer experiences

    Designed for quick iteration and easy publishing through the VIVERSE / Wonderland tooling.

    Live Demo:

    hashtag
    Features

    The example project includes the following key features:

    • VIVERSE Avatar SDK Integration: Login, avatar spawn, and user metadata management

    • Wonderland Cloud Networking: Client + server template for multiplayer functionality

    • VIVERSE CLI Plugin: Simple integration for publishing and previewing projects

    hashtag
    Project Structure

    The repository contains the following directories and files:

    • js/ - JavaScript/TypeScript source files

    • models/ - 3D model assets

    • plugins/viverse-publish-plugin/ - VIVERSE CLI plugin for publishing

    hashtag
    Quick Setup (Editor)

    1

    hashtag
    Open the plugin

    Open the viversePublishPlugin inside the Wonderland Editor.

    2

    hashtag
    Scene Configuration

    1

    hashtag
    Navigate to Avatar Component

    In the Wonderland Editor hierarchy, navigate to: Avatar -> VrmDynamic and select the VrmDynamic object.

    2

    hashtag
    Networking Setup (Wonderland Cloud)

    1

    hashtag
    Access Wonderland Cloud

    Open your web browser and navigate to: https://cloud.wonderland.dev/create-server. Log in with your Wonderland Cloud credentials if required.

    2

    hashtag
    Publish & Preview

    1

    hashtag
    Open VIVERSE CLI Plugin

    In the Wonderland Editor, open the VIVERSE CLI plugin.

    2

    hashtag
    Component Configuration Details

    hashtag
    VIVERSE Provider Component

    The Viverse Provider component (or viverse-provider-component) handles authentication and avatar management:

    • App ID: Your VIVERSE application identifier (required)

    • Avatar Spawning: Automatically spawns user avatars based on VIVERSE account

    • User Metadata: Retrieves and stores user name and avatar data

    hashtag
    Simple Example Client Component

    The simple-example-client component manages networking:

    • Server Path: WebSocket URL to your Wonderland Cloud server (required)

    • Connection Management: Handles connection/disconnection events

    • Avatar Replication: Syncs avatar positions and states across clients

    hashtag
    Troubleshooting

    hashtag
    Avatars Not Appearing

    Problem: Avatars don't spawn or appear in the scene.

    Solutions:

    • Verify the App ID is set correctly in both:

      • The VIVERSE Publish Plugin

      • The VrmDynamic -> Viverse Provider component

    hashtag
    Networking Failures

    Problem: Multiplayer connectivity issues or connection errors.

    Solutions:

    • Re-check that the uploaded server package matches the server entry on cloud.wonderland.dev

    • Confirm the server path is pasted correctly in the simple-example-client component

    • Verify the server path format (should start with wss://

    hashtag
    Preview URL Issues

    Problem: Preview URL doesn't work or shows errors.

    Solutions:

    • Use the plugin Preview URL to test cross-device connectivity before submitting

    • Check that the build was published successfully

    • Verify all required assets are included in the build

    hashtag
    Browser Console Errors

    Problem: Errors appear in browser console (F12).

    Solutions:

    • Check browser console logs for helpful client-side errors

    • Common issues include:

      • Auth errors: Verify App ID and VIVERSE credentials

    hashtag
    Server Upload Issues

    Problem: Server package upload fails or server doesn't deploy.

    Solutions:

    • Verify the server package file exists: server/wonderland-cloud-example-simple-1.0.0.tgz

    • Check file size limits on Wonderland Cloud

    • Ensure the package is not corrupted

    hashtag
    Build and Publishing Issues

    Problem: Build fails or publishing doesn't complete.

    Solutions:

    • Ensure all required dependencies are installed

    • Check that all scene files are saved

    • Verify project structure matches expected format

    hashtag
    Additional Resources

    hashtag
    Documentation Links

    • Wonderland Engine Documentation:

    • VIVERSE Documentation:

    • Wonderland Cloud:

    hashtag
    Support

    If you need help setting up this template for your project:

    • Reach out via Discord (Wonderland Engine community)

    • Check the GitHub repository for issues and discussions

    • Review the example code in the repository for implementation details

    hashtag
    Contributing

    Pull requests are welcome for improvements to this template:

    • Keep changes small and well-documented

    • Open an issue for feature requests or bugs

    • Follow the existing code style and structure

    hashtag
    Technical Details

    hashtag
    Project Dependencies

    The project uses the following key technologies:

    • Wonderland Engine: Web-based 3D engine for VR/AR experiences

    • VIVERSE Avatar SDK: Avatar system and user authentication

    • Wonderland Cloud: Multiplayer networking infrastructure

    hashtag
    File Structure Details

    • js/ - Contains component scripts and application logic

    • models/ - 3D models and avatar assets

    • server/ - Wonderland Cloud server implementation

    hashtag
    Build Process

    1. Development: Edit scenes and components in Wonderland Editor

    2. Configuration: Set App ID and server path in components

    3. Publishing: Use VIVERSE CLI plugin to build and upload

    hashtag
    Multiplayer Architecture

    The example uses a client-server architecture:

    • Client: Wonderland Engine application running in browser

    • Server: Wonderland Cloud server handling networking

    • Communication: WebSocket connection for real-time updates

    import { Script } from 'playcanvas';
    import * as pc from 'playcanvas';
    import { PlayerService } from '../@viverse/create-sdk.mjs'
    
    
    export class LocalPlayerManager extends Script 
    {
        initialize() {
            this.playerService  = new PlayerService();
            
            //attach playerService for global access, reference this.app.playerServiceManager in other files
            this.app.playerServiceManager = this;
            
            //FOR ALL CUSTOMIZABLE PROPS AND METHODS, see: https://viveportsoftware.github.io/pc-lib/interfaces/ILocalPlayer.html
            
            //enable flight
            this.playerService.localPlayer.canFly = true;
            
            //enable movement
            this.playerService.localPlayer.canMove = true;
            
            //hide avatar
            this.playerService.localPlayer._entity.visibility = false;
        }
    
        update(dt)
        {
        }
    }
    import { Script } from 'playcanvas';
    import * as pc from "playcanvas"
    import { CameraService } from './@viverse/create-sdk.mjs'
    
    /**
     * The {@link https://api.playcanvas.com/classes/Engine.Script.html | Script} class is
     * the base class for all PlayCanvas scripts. Learn more about writing scripts in the
     * {@link https://developer.playcanvas.com/user-manual/scripting/ | scripting guide}.
     */
    export class CameraServiceManager extends Script {
        /**
         * Called when the script is about to run for the first time.
         */
        initialize() {
            this.cameraService  = new CameraService();
            
            //FOR ALL CUSTOMIZABLE PROPS AND METHODS, see https://viveportsoftware.github.io/pc-lib/interfaces/ICameraService.html
            
            //switch to 1st person pov
            this.cameraService.switchPov(0);
            
            //prevent pov switching
            this.cameraService.canSwitchPov = false;
        }
    
        /**
         * Called for enabled (running state) scripts on each tick.
         * 
         * @param {number} dt - The delta time in seconds since the last frame.
         */
        update(dt) {
        }
    }
    // To include the File System module, use the require() method
    const fs = require('fs');
    
    // Using the File System module to read files
    fs.readFile();
    
    // Exporting the greet function so it can be used in other modules, use module.exports
    module.exports = function greet(name) {
        return 'Hello, ${name}!';
    };
    // To include the File System module, you no longer use require() method
    // Use import instead.
    import fs from 'fs';
    
    // Using the File System module to read files
    fs.readFile();
    
    // Exporting the greet function so it can be used in other modules, you no longer
    // use `module.exports`. Use `export` instead.
    export const greet = (name) => {
        return 'Hello, ${name}!';
    };
    import { Script } from 'playcanvas';
    
    export class GameManager extends Script {
        /**
         * Called when the script is about to run for the first time.
         */
        initialize() {
        }
    
        /**
         * Called for enabled (running state) scripts on each tick.
         * 
         * @param {number} dt - The delta time in seconds since the last frame.
         */
        update(dt) {
        }
    }
    import { Script } from 'playcanvas';
    
    export class DoorRotator extends Script {
        /**
         * @attribute
         * @type {number}
         * @title First Number
         */
        firstNumber = 10;
        
        initialize() {
            // Add the DoorRotator script to this.app
            this.app.doorRotator = this;
        }
    
        rotateDoor() {
            console.log('Rotate Door!');
        }
    }
    var TriggerArea = pc.createScript('triggerArea');
    
    TriggerArea.prototype.initialize = function() {
        // Setup listening for the triggerenter event
        this.entity.collision.on('triggerenter', this.onTriggerEnter, this);
    };
    
    // Handle the onTriggerEnter event
    TriggerArea.prototype.onTriggerEnter = function(entity) {
        // Calling function in DoorRotator.mjs
        this.app.doorRotator.rotateDoor();
    
        // Accessing property in DoorRotator.mjs
        console.log("FirstNumber: " + this.app.doorRotator.firstNumber);
    }
    import { Script } from 'playcanvas';
    
    export class Debug extends Script {
        /**
         * Called when the script is about to run for the first time.
         */
        initialize() {
            debugger;
            console.log("Debug initialize!");
    
            this.testFunction();
        }
    
        testFunction() {
            debugger;
            console.log('Test Function!');
        }
    }
    import { Script } from 'playcanvas';
    
    export class GameManager extends Script {
        /**
         * @attribute
         * @type {number}
         * @title First Number
         */
        firstNumber
    
        /**
         * @attribute
         * @type {number}
         * @title Second Number
         */
        secondNumber = 5;
    
        /**
         * @attribute
         * @type {string}
         * @title First String
         */
        firstString
    
        /**
         * @attribute
         * @type {string}
         * @title Second String
         */
        secondString = 'Default Text';
    
        /**
         * @attribute
         * @type {boolean}
         * @title First Boolean
         */
        firstBoolean
    
        /**
         * @attribute
         * @type {boolean}
         * @title Second Boolean
         */
        secondBoolean = false;
    
        /**
        * @attribute
        * @type { pc.Entity }
        * @title Entity
        */
        entity
    
        /**
         * @attribute
         * @type {number[]}
         * @title Number Array
         */
        numberArray
    
        /**
         * @attribute
         * @type {string[]}
         * @title String Array
         */
        stringArray
    
        /**
         * @attribute
         * @type {boolean[]}
         * @title Boolean Array
         */
        booleanArray
    
        /**
        * @attribute
        * @type { pc.Entity[] }
        * @title Entities
        */
        entityArray
    
        /**
         * @attribute
         * @type { CustomObject[] }
         * @title Custom Objects Array
         */
        customObjectArray
    
        /**
         * Called when the script is about to run for the first time.
         */
        initialize() {
            this.changeFirstNumber();
            this.printValue("FirstNumber: " + this.firstNumber);
            this.printValue("SecondNumber: " + this.secondNumber);
    
            this.modifyFirstString();
            this.printValue("FirstString: " + this.firstString);
            this.printValue("SecondString: " + this.secondString);
    
            this.modifyFirstBoolean();
            this.printValue("FirstBoolean: " + this.firstBoolean);
            this.printValue("SecondBoolean: " + this.secondBoolean);
    
            this.printValue("Entity Name: " + this.entity.name);
    
            this.printValue("Number Array: ");
            this.printArray(this.numberArray);
    
            this.printValue("String Array: ");
            this.printArray(this.stringArray);
    
            this.printValue("Boolean Array: ");
            this.printArray(this.booleanArray);
    
            this.printValue("Entity Array: ");
            this.printEntityArray(this.entityArray);
    
            this.printValue("CustomObjectArray: ");
            this.printCustomObjectArray(this.customObjectArray);
        }
    
        /**
         * Called for enabled (running state) scripts on each tick.
         * 
         * @param {number} dt - The delta time in seconds since the last frame.
         */
        update(dt) {
        }
    
        changeFirstNumber() {
            this.firstNumber = 3;
        }
    
        modifyFirstString() {
            this.firstString = "modified string";
        }
    
        modifyFirstBoolean() {
            this.firstBoolean = true;
        }
    
        printValue(value) {
            console.log(value);
        }
    
        printArray(array) {
            for (var x = 0; x < array.length; x++) {
                console.log(array[x]);
            }
        }
    
        printEntityArray(array) {
            for (var x = 0; x < array.length; x++) {
                console.log(array[x].name);
            }
        }
    
        printCustomObjectArray(array) {
            for (var x = 0; x < array.length; x++) {
                console.log(array[x].valueOf());
            }
        }
    }
    
    /** @interface */
    class CustomObject {
        /**
         * @attribute
         * @type { number }
         * @title Number
         */
        number
    
        /**
         * @attribute
         * @type { pc.Entity[] }
         * @title Entities Array
         */
        entities
    }
    ```
    Example Components: Pre-configured components showing where to place appid and server path
  • Spatial Audio: Avatar position replication with spatial audio for medium-scale sessions

  • server/ - Wonderland Cloud server package

  • shaders/ - Custom shader files

  • static/ - Static assets

  • app.js - Main application entry point

  • package.json - Node.js dependencies and scripts

  • tsconfig.json - TypeScript configuration

  • viversecli.wlp - VIVERSE CLI configuration file

  • hashtag
    Log In with VIVERSE Credentials

    3

    hashtag
    Create Application

    Click Create Application — this redirects to VIVERSE Studio.

    4

    hashtag
    Copy the App ID

    In VIVERSE Studio create an app and copy the App ID.

    5

    hashtag
    Paste the App ID

    Paste the App ID into the appid field of the plugin.

    hashtag
    Expand VIVERSE Provider Component

    In the Inspector, expand the Viverse Provider component (or viverse-provider-component).

    3

    hashtag
    Paste the App ID

    Locate the appid field in the component properties and paste the App ID that you copied from VIVERSE Studio.

    4

    hashtag
    Verify Configuration

    Double-check that the App ID is correctly set in both the VIVERSE Publish Plugin and the VrmDynamic -> Viverse Provider component, then save the scene to preserve your changes.

    hashtag
    Locate Server Package

    Locate the server package in your project: server/wonderland-cloud-example-simple-1.0.0.tgz and verify the file exists and is not corrupted.

    3

    hashtag
    Upload Server Package

    In the Wonderland Cloud interface, click Upload or Create Server, select the file: server/wonderland-cloud-example-simple-1.0.0.tgz, and wait for the upload to complete. Wonderland Cloud will process and deploy your server.

    4

    hashtag
    Copy Server Path

    After the server is created, Wonderland Cloud will display server information. Copy the server path that is provided (typically looks like: wss://your-server-id.wonderland.dev).

    5

    hashtag
    Configure Client Component

    Return to the Wonderland Editor, select the Player object in the hierarchy, locate the simple-example-client component in the Inspector, find the serverPath field, paste the server path you copied from Wonderland Cloud, and save the scene.

    hashtag
    Publish Project

    Click Publish to build and upload your project. Wait for the publishing process to complete. The plugin will show the status of the upload.

    3

    hashtag
    Preview Your Project

    After publishing, click Preview URL in the plugin. This will open a preview build in your browser. Test all functionality including avatar spawning, multiplayer connectivity, spatial audio, and user interactions.

    4

    hashtag
    Submit for Review

    In the VIVERSE CLI plugin, click Submit for Review. This will open the VIVERSE Creator page for your application. Complete any required information in the VIVERSE Creator interface and review your application settings before submission.

    5

    hashtag
    Guest Preview Testing

    From the VIVERSE Creator page, use the Guest Preview link. Share this link with others for testing. Test multiplayer functionality with multiple devices/accounts, verify that avatars appear correctly for all users, and test spatial audio and networking in production-like conditions.

    6

    hashtag
    Final Submission

    Once testing is complete and you're satisfied with the build, submit the app for review to list it publicly. Wait for VIVERSE approval. After approval, your application will be available in VIVERSE.

    Presence State: Manages user presence in the multiplayer session

    Check that the App ID matches the one from VIVERSE Studio

  • Ensure you're logged in with a valid VIVERSE account

  • Check browser console (F12) for authentication errors

  • Verify CORS settings in VIVERSE Studio if using custom domains

  • )
  • Check that the server is running and accessible

  • Review browser console (F12) for WebSocket connection errors

  • Ensure firewall/network settings allow WebSocket connections

  • Clear browser cache and try again
  • Test in different browsers (Chrome, Firefox, Edge)

  • CORS errors: Check VIVERSE Studio CORS settings
  • Network errors: Verify server path and connectivity

  • Look for specific error messages and error codes

  • Check network tab for failed requests

  • Try re-uploading the server package
  • Check Wonderland Cloud dashboard for server status

  • Review build logs for specific errors
  • Try rebuilding the project from scratch

  • Check Wonderland Editor console for errors

  • TypeScript/JavaScript: Application logic and components

    plugins/viverse-publish-plugin/ - Editor integration for publishing

  • static/ - Static web assets (images, fonts, etc.)

  • shaders/ - Custom shader code for rendering

  • Deployment: Preview and submit through VIVERSE Creator
    State Sync: Avatar positions and presence state synchronized across clients
    https://worlds.viverse.com/k99xkYZarrow-up-right
    https://wonderlandengine.com/arrow-up-right
    https://docs.viverse.com/arrow-up-right
    https://cloud.wonderland.dev/arrow-up-right
    https://github.com/WonderlandEngine/viverse-example/tree/mainarrow-up-right
    Polygon Streaming Project Settings
    integrating VIVERSE SDKs
    See here
    Introduction to Developer Tools
    See here
    See here
    See here

    Unity WebGL

    Code examples, compatibility guides, and technical documentation for Unity WebGL builds targeting VIVERSE. Includes render pipelines, WebGL templates, loading screens, and deployment examples.


    hashtag
    Publishing Tutorial

    Anyone can publish their WebGL-compatible Unity project to VIVERSE in a few simple steps. In this guide, we'll walk through the process of creating an new Unity project, making sure it is compatible with WebGL, and publishing to VIVERSE using the VIVERSE CLIarrow-up-right.

    circle-info

    For this guide, we are using the VIVERSE CLI, but it is also possible to compress and .

    While VIVERSE is a great place for multiplayer games with networked avatars — and we have a number of services that can help you implement these features — it is not required to implement networked avatars to publish to VIVERSE.

    hashtag
    Prerequisites

    • Unity Hub and Unity installed on your device.

    • and installed on your device. Please use at least Node v22 - Only required if using the CLI, not VIVERSE Studio

    circle-exclamation

    In this tutorial, we will be using Unity v6.1, however any WebGL-compatible version of Unity should be supported.

    hashtag
    Configure Your Unity Project

    1

    hashtag
    Create a Unity Project

    2

    hashtag
    Build and Publish to VIVERSE

    1

    hashtag
    Build Your Project

    Navigate to Publish and select "WebGL Publish". In the pop-up, click "Build and Publish", selecting the desired folder for your build. When doing this for the first time, Unity will automatically publish to their web-servers for testing. For future builds, you can disable this behavior to just the builds without publishing.

    2

    hashtag
    Unity & VIVERSE Compatibility Overview

    VIVERSE supports Unity WebGL builds with specific requirements and recommendations for optimal performance and compatibility. This section covers essential settings and considerations for targeting VIVERSE.

    hashtag
    Supported Unity Versions

    VIVERSE supports the following Unity versions:

    • Unity 2021 LTS (2021.3.x) - Recommended for stability

    • Unity 2022 LTS (2022.3.x) - Recommended for newer features

    • Unity 2023.x - Supported, but verify compatibility with VIVERSE SDK

    Note: Always use LTS (Long Term Support) versions for production projects targeting VIVERSE to ensure long-term compatibility and support.

    hashtag
    Required Unity Modules

    Install the following modules via Unity Hub:

    1. WebGL Build Support - Essential for building WebGL projects

    2. WebGL Development Build Support - Recommended for debugging

    3. IL2CPP - Required for WebGL builds (automatically included with WebGL module)

    Verify installation: Edit → Preferences → External Tools or check installed modules in Unity Hub.

    hashtag
    Supported Render Pipelines

    Important: Unity WebGL does NOT support HDRP (High Definition Render Pipeline).

    Built-in Render Pipeline:

    • Fully supported for WebGL/VIVERSE

    • Best compatibility across all browsers

    • Recommended for projects targeting maximum compatibility

    Universal Render Pipeline (URP):

    • Fully supported for WebGL/VIVERSE

    • Recommended for new projects targeting WebGL

    • Better performance and modern features optimized for web platforms

    High Definition Render Pipeline (HDRP):

    • NOT supported on WebGL/VIVERSE

    • HDRP is designed for high-end GPUs and requires features unavailable in WebGL:

      • Real-time ray tracing

    hashtag
    Choosing a Render Pipeline

    For New Projects Targeting WebGL/VIVERSE:

    • URP is strongly recommended for modern features and optimal WebGL performance

    • Install via Window → Package Manager → Unity Registry → Universal RP

    For Existing Projects:

    • Built-in: Works without changes, but URP offers better WebGL performance

    • URP Migration: Use Unity's Render Pipeline Converter tool (Edit → Render Pipeline → Universal Render Pipeline → Convert Project to URP)

    • HDRP Projects: Must convert to URP for WebGL builds (HDRP is not supported)

    Converting HDRP to URP for WebGL:

    1. Install URP package via Package Manager

    2. Use Unity's Render Pipeline Converter:

      • Edit → Render Pipeline → Universal Render Pipeline → Convert Project to URP

    Considerations:

    • URP provides better WebGL performance than Built-in

    • URP has smaller build sizes

    • URP supports modern shader features optimized for web

    hashtag
    Render Pipeline Settings

    Built-in Render Pipeline:

    • No special configuration needed

    • Settings in Edit → Project Settings → Graphics

    • Configure Quality Settings as needed

    Universal Render Pipeline (URP):

    • Create URP Asset: Assets → Create → Rendering → URP Asset

    • Assign in Edit → Project Settings → Graphics → Scriptable Render Pipeline Settings

    • Configure URP Asset settings:

    hashtag
    Shader Compatibility

    Built-in Shaders:

    • All built-in shaders work with Built-in Render Pipeline

    • Standard, Unlit, UI shaders fully supported

    URP Shaders:

    • Use URP-compatible shaders (Lit, Unlit, etc.)

    • Built-in shaders won't work with URP

    • Convert shaders using Shader Graph or manually rewrite

    Custom Shaders:

    • Ensure shaders are compatible with your chosen render pipeline

    • HDRP shaders cannot be used in WebGL builds - convert to URP equivalents

    • Test shaders in WebGL build (some features may not work)

    hashtag
    Unity Formatting and Custom Loading Screens

    An example of a fullscreen WebGL template can be found . The instructions for creating a WebGL fullscreen template are below.

    hashtag
    Understanding WebGL Templates

    WebGL templates control the HTML/CSS/JavaScript wrapper around your Unity WebGL build. They determine:

    • How the canvas is displayed (fullscreen, windowed, etc.)

    • Loading screen appearance and behavior

    • Browser UI elements visibility

    Default Templates:

    • Unity provides default templates (Minimal, Default, etc.)

    • Custom templates allow full control over presentation

    hashtag
    Fullscreen Canvas Benefits

    A fullscreen canvas template provides:

    • Immersive Experience: No browser UI distractions

    • Maximum Screen Usage: Canvas fills entire viewport

    • Better Performance: No layout calculations for browser elements

    Key Features:

    • Canvas set to 100% width and height

    • Body and container with overflow: hidden

    • No scrollbars or browser chrome

    hashtag
    Custom Loading Screen Overview

    Custom loading screens enhance user experience by:

    • Branding: Display your logo/branding during load

    • Progress Feedback: Show loading progress to users

    • Professional Appearance: Custom design matching your project

    Components:

    • Loading bar container (centered on screen)

    • Logo/branding element

    • Progress bar (animates from 0% to 100%)

    hashtag
    Create the WebGL Template Folder Structure

    1

    hashtag
    Navigate to Assets Folder

    In Unity, open your project and navigate to the Assets folder in the Project window.

    2

    hashtag
    Create the Fullscreen HTML Template

    1

    hashtag
    Create index.html File

    1. Right-click on the Fullscreen folder → Show in Explorer (Windows) or Reveal in Finder (Mac)

    hashtag
    Customize the Loading Screen

    1

    hashtag
    Understanding the Loading Screen Elements

    The template includes several customizable elements:

    hashtag
    Configure Build Settings

    1

    hashtag
    Open Build Settings

    1. In Unity, go to File → Build Settings...

    hashtag
    Build the WebGL Project

    1

    hashtag
    Prepare for Build

    1. Ensure your scene is saved (Ctrl+S / Cmd+S

    hashtag
    Test the Fullscreen Build Locally

    1

    hashtag
    Set Up Local Server

    WebGL builds require a web server to run (cannot open index.html directly due to CORS restrictions).

    Option 1: Python HTTP Server (if Python installed):

    hashtag
    Deploy to VIVERSE

    1

    hashtag
    Prepare Build for Upload

    1. Navigate to your build output folder

    hashtag
    Advanced Customization

    1

    hashtag
    Customize Background Color

    Modify the canvas background in the CSS:

    2

    hashtag
    Troubleshooting

    Template Not Appearing in Build Settings:

    • Ensure the folder is named exactly WebGLTemplates (case-sensitive)

    • Ensure index.html is directly in the template folder (e.g., Assets/WebGLTemplates/FullScreen/index.html)

    Loading Screen Not Showing:

    • Check browser console for JavaScript errors (F12)

    • Verify image paths in CSS are correct

    • Ensure loadingBar.style.display = "block" is called before Unity loads

    Fullscreen Not Working:

    • Desktop: verify canvas has width/height 100% (set in JS in this template) and html/body/#unity-container fill the viewport

    • Mobile:

    Build Too Large:

    • Enable compression in Publishing Settings

    • Increase Managed Stripping Level

    • Remove unused assets and scripts

    VIVERSE Upload Issues:

    • Ensure ZIP contains index.html at root level

    • Verify all required files are included (Build folder, etc.)

    • Check file size limits in VIVERSE Studio

    hashtag
    Install the Unity Plugin

    Navigate to Window > Package Manager > Unity Registry and search for "WebGL Publisher". Add the module to your project.

    3

    hashtag
    (Optional) Enable Decompression Fallback

    Compression is supported on VIVERSE, however you can enable fallback if you are encountering errors or would like to have it included. Navigate to Edit > Project Settings > Player > Web Settings > Publishing Settings and check "Decompression Fallback".

    hashtag
    Install the VIVERSE CLI

    In a terminal session, run npm install -g @viverse/cli to install the CLI globally. Make sure you are using at least node v22.

    3

    hashtag
    Login to VIVERSE

    In the terminal session, run viverse-cli auth login and enter your VIVERSE account email and password.

    4

    hashtag
    Create VIVERSE App

    In the terminal session, run viverse-cli app create . Once complete, copy the app ID to be used when publishing.

    5

    hashtag
    Publish to VIVERSE

    In the terminal session, run viverse-cli app publish {path/to/unity/webgl/build} --app-id {your app id from step 4} referencing folder containing the index.html of your Unity build.

    6

    hashtag
    Test & Configure World Settings

    Navigate to the preview url created for the world. You can also access the world and its settings in studio.viverse.com/contentarrow-up-right.

    7

    hashtag
    Submit for Curation and Discovery

    By default, worlds uploaded will only be accessible via preview urls. For placement and curation on our webpages, meaning your experience will be easier to share, please submit for review.

    No additional packages required
    Requires URP package (installed via Package Manager)
  • Optimized for low-end and web platforms

  • Advanced volumetrics
  • Compute shaders with full GPU access

  • Large render targets

  • Advanced shader models

  • WebGL runs in a browser sandbox with restricted GPU access and memory

  • You must convert HDRP projects to URP for WebGL builds

  • Review and update materials (HDRP materials need URP equivalents)

  • Test shaders and lighting (some HDRP features may not have direct URP equivalents)

  • Optimize for WebGL performance (WebGL is sensitive to draw calls, textures, and shader complexity)

  • Built-in has maximum compatibility but lower performance
  • HDRP cannot be used for WebGL builds

  • Render Scale: 1.0 (or lower for performance)

  • HDR: Disabled (WebGL doesn't support HDR)

  • MSAA: 2x or 4x (balance quality vs performance)

  • Shadow Distance: Adjust based on your scene needs

  • HDRP shaders are NOT compatible - must convert to URP shaders for WebGL
    WebGL 2.0 supports most modern shader features, but not HDRP-specific features
  • WebGL is sensitive to shader complexity - optimize for performance

  • Mobile responsiveness
  • Fullscreen functionality

  • Professional Appearance: Clean, game-like presentation
  • VIVERSE Optimization: Matches VIVERSE's immersive environment

  • Responsive to different screen sizes
  • Mobile-friendly viewport configuration

  • User Engagement: Keep users informed during asset loading
    Optional loading text or animations
  • Styled with CSS for custom appearance

  • hashtag
    Create WebGLTemplates Directory

    1. Right-click in the Assets folder → Create → Folder

    2. Name the folder WebGLTemplates (exact name required by Unity)

    3. This folder will contain all custom WebGL templates

    3

    hashtag
    Create Fullscreen Template Folder

    1. Right-click on WebGLTemplates → Create → Folder

    2. Name it Fullscreen (this will be the template name visible in Build Settings)

    3. Unity will automatically detect this folder as a WebGL template

    Create a new text file named index.html (not index.html.txt)

  • This file will serve as the main HTML template for your WebGL build

  • 2

    hashtag
    Add Fullscreen HTML Structure

    Open index.html and add the following fullscreen template code:

    Key Features:

    • html, body, and #unity-container fill the viewport with overflow: hidden for true fullscreen

    • Desktop: canvas style width/height set to 100% in JavaScript

    • Mobile (VIVERSE-friendly): full-viewport fixed container, canvas 100% via CSS, viewport meta tag, and config.devicePixelRatio = 1 to reduce high-DPI backing-store cost

    • Loading bar centered on screen during asset loading

    • Footer hidden by default (can be shown if needed)

    3

    hashtag
    Refresh Unity Project

    1. Return to Unity Editor

    2. The index.html file should appear in the Project window under Assets/WebGLTemplates/Fullscreen/

    3. If not visible, right-click in Project window → Refresh, or press Ctrl+R (Windows) / Cmd+R (Mac)

    #unity-loading-bar: Container for loading screen elements
  • #unity-logo: Logo image displayed during loading

  • #unity-progress-bar-empty: Background of the progress bar

  • #unity-progress-bar-full: Animated progress bar that fills from 0% to 100%

  • All elements are centered on screen using CSS transforms.

    2

    hashtag
    Replace Default Loading Images

    1. In the Fullscreen folder, replace the default Unity loading images with your custom assets:

      • unity-logo-dark.png (154×130px recommended) - Your logo/branding

      • progress-bar-empty-dark.png (141×18px) - Progress bar background

      • progress-bar-full-dark.png (141×18px) - Progress bar fill image

    2. Alternatively, modify the CSS in index.html to use different image paths or create custom loading UI with HTML/CSS instead of images.

    3

    hashtag
    Custom Loading Screen with HTML/CSS

    To create a fully custom loading screen without images, modify the #unity-loading-bar section:

    Update the JavaScript progress callback to also update text:

    4

    hashtag
    Add Custom Loading Animations

    Enhance the loading screen with CSS animations. Add to the <style> section:

    Select WebGL from the Platform list

  • Click Switch Platform if not already on WebGL (this may take a few minutes)

  • 2

    hashtag
    Select the Fullscreen Template

    1. Click Player Settings... (or go to Edit → Project Settings → Player)

    2. In the Player Settings window, expand the Publishing Settings section

    3. Under WebGL Template, select Fullscreen from the dropdown

    4. The template you created should now be available in this list

    3

    hashtag
    Configure Additional WebGL Settings

    For optimal VIVERSE deployment, configure these settings:

    Resolution and Presentation:

    • Default Canvas Width: 1920 (or your target resolution)

    • Default Canvas Height: 1080 (or your target resolution)

    • Run In Background: Enabled (recommended for VIVERSE)

    Publishing Settings:

    • Compression Format: Gzip or Brotli (for smaller file sizes)

    • Data caching: Enabled (improves load times for returning users)

    • Name Files As Hashes: Enabled (better caching)

    Other Settings:

    • Strip Engine Code: Enabled (reduces build size)

    • Managed Stripping Level: Medium or High (further reduces size)

    )
  • In Build Settings, verify the scenes you want to include are checked

  • Click Add Open Scenes if your current scene isn't listed

  • 2

    hashtag
    Build the Project

    1. Click Build in the Build Settings window

    2. Choose or create an output folder (e.g., Builds/WebGL)

    3. Click Select Folder

    4. Unity will compile and build your project (this may take several minutes)

    5. Once complete, navigate to the build output folder

    3

    hashtag
    Verify Build Output

    Your build folder should contain:

    • index.html - Your custom fullscreen template

    • Build/ folder - Contains Unity WebGL build files (.data, .framework.js, .loader.js, etc.)

    • TemplateData/ folder (if using default template) - Contains template assets

    • StreamingAssets/ folder (if you have streaming assets) - Contains additional assets

    The index.html should be your custom fullscreen template with the loading screen.

    Then open http://localhost:8000 in your browser.

    Option 2: Node.js HTTP Server:

    Option 3: Unity's Built-in Server: After building, Unity may offer to open a local server automatically.

    2

    hashtag
    Test Fullscreen Functionality

    1. Open the build in your browser

    2. Verify the loading screen appears and progress bar animates

    3. Once loaded, the canvas should fill the entire browser window

    4. Test the fullscreen button (if visible) or press F11 for browser fullscreen

    5. Verify no browser UI elements are visible (address bar, scrollbars, etc.)

    6. Test on different screen resolutions and aspect ratios

    3

    hashtag
    Test Mobile Responsiveness

    1. Open browser developer tools (F12)

    2. Enable device emulation mode

    3. Test on various mobile device presets (iPhone, Android, tablets)

    4. Verify the viewport meta tag works correctly

    5. Test touch interactions if your project uses them

    Select all files and folders (index.html, Build/, StreamingAssets/ if present)

  • Create a ZIP archive of these files

  • Name it appropriately (e.g., MyProject_WebGL_Build.zip)

  • Verify the ZIP contains the root index.html file (not nested in a subfolder)

  • 2

    hashtag
    Upload to VIVERSE Studio

    1. Log in to VIVERSE Studio (https://worlds.viverse.com/)

    2. Navigate to Manage Content or your project dashboard

    3. Click Upload or New Content

    4. Select your ZIP file

    5. Wait for upload and processing to complete

    3

    hashtag
    Configure VIVERSE Settings

    1. In VIVERSE Studio, configure your content settings:

      • Set appropriate title and description

      • Configure access permissions (public/private)

      • Set thumbnail/preview image

      • Configure any required VIVERSE SDK settings

    2. Ensure WebGL compatibility is enabled

    4

    hashtag
    Preview and Publish

    1. Use VIVERSE Studio's preview feature to test your content

    2. Verify fullscreen behavior works correctly in VIVERSE environment (test on mobile as well as desktop; layout and GPU memory behave differently on phones)

    3. Test loading screen appearance and timing

    4. Once satisfied, submit for approval/publishing

    5. After approval, your content will be available in VIVERSE

    hashtag
    Add Splash Screen or Branding

    Add custom HTML before the canvas loads:

    Then hide it in JavaScript after Unity loads:

    3

    hashtag
    Implement Custom Error Handling

    Enhance error messages for better user experience:

    4

    hashtag
    Optimize Loading Performance

    1. Enable Compression: In Player Settings → Publishing Settings, use Gzip or Brotli compression

    2. Reduce Build Size: Enable code stripping and remove unused assets

    3. Use Addressables: For large projects, consider Unity Addressables for on-demand loading

    4. Optimize Assets: Compress textures, reduce polygon counts, optimize audio files

    5. CDN Hosting: Host your build on a CDN for faster global loading times

    Refresh Unity project (
    Ctrl+R
    /
    Cmd+R
    )
  • Restart Unity Editor

  • Check that progress callback is properly connected

    ensure
    #unity-container.unity-mobile
    and
    .unity-mobile #unity-canvas
    rules are present (small canvas or black bars usually mean mobile layout CSS is missing)
  • If WebGL fails or stutters on high-DPI phones, confirm config.devicePixelRatio = 1 runs in the mobile branch

  • Test in different browsers (Chrome, Firefox, Edge)

  • Some browsers require user interaction before allowing fullscreen API

  • Consider using Unity Addressables for large content
  • Compress textures and audio files

  • Ensure WebGL build target is correct (not IL2CPP if not supported)
    upload your build file directly to the VIVERSE Studio
    Nodearrow-up-right
    npmarrow-up-right
    herearrow-up-right
    <!DOCTYPE html>
    <html lang="en-us">
      <head>
        <meta charset="utf-8">
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>Unity WebGL Player | {{{ PRODUCT_NAME }}}</title>
        <style>
          html, body {
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
            overflow: hidden;
          }
          #unity-container {
            position: absolute;
            width: 100%;
            height: 100%;
            overflow: hidden;
          }
          #unity-container.unity-mobile {
            position: fixed;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
          }
          .unity-mobile #unity-canvas {
            width: 100%;
            height: 100%;
            display: block;
          }
          #unity-canvas {
            background: #231F20;
          }
          #unity-loading-bar {
            position: absolute;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
            display: none;
          }
          #unity-logo {
            width: 154px;
            height: 130px;
            background: url('unity-logo-dark.png') no-repeat center;
          }
          #unity-progress-bar-empty {
            width: 141px;
            height: 18px;
            margin-top: 10px;
            margin-left: 6.5px;
            background: url('progress-bar-empty-dark.png') no-repeat center;
          }
          #unity-progress-bar-full {
            width: 0%;
            height: 18px;
            margin-top: 10px;
            background: url('progress-bar-full-dark.png') no-repeat center;
          }
          #unity-footer {
            position: relative;
            display: none;
          }
          .unity-mobile #unity-footer {
            display: none;
          }
          #unity-webgl-logo {
            float: left;
            width: 204px;
            height: 38px;
            background: url('webgl-logo.png') no-repeat center;
          }
          #unity-build-title {
            float: right;
            margin-right: 10px;
            line-height: 38px;
            font-family: arial;
            font-size: 18px;
          }
          #unity-fullscreen-button {
            cursor: pointer;
            float: right;
            width: 38px;
            height: 38px;
            background: url('fullscreen-button.png') no-repeat center;
          }
          #unity-warning {
            position: absolute;
            left: 50%;
            top: 5%;
            transform: translate(-50%);
            background: white;
            padding: 10px;
            display: none;
          }
        </style>
      </head>
      <body>
        <div id="unity-container" class="unity-desktop">
          <canvas id="unity-canvas" tabindex="-1"></canvas>
          <div id="unity-loading-bar">
            <div id="unity-logo"></div>
            <div id="unity-progress-bar-empty">
              <div id="unity-progress-bar-full"></div>
            </div>
          </div>
          <div id="unity-warning"> </div>
          <div id="unity-footer">
            <div id="unity-webgl-logo"></div>
            <div id="unity-fullscreen-button"></div>
            <div id="unity-build-title">{{{ PRODUCT_NAME }}}</div>
          </div>
        </div>
        <script>
          var container = document.querySelector("#unity-container");
          var canvas = document.querySelector("#unity-canvas");
          var loadingBar = document.querySelector("#unity-loading-bar");
          var progressBarFull = document.querySelector("#unity-progress-bar-full");
          var fullscreenButton = document.querySelector("#unity-fullscreen-button");
          var warningBanner = document.querySelector("#unity-warning");
    
          function unityShowBanner(msg, type) {
            function updateBannerVisibility() {
              warningBanner.style.display = warningBanner.children.length ? 'block' : 'none';
            }
            var div = document.createElement('div');
            div.innerHTML = msg;
            warningBanner.appendChild(div);
            if (type == 'error') div.style = 'background: red; padding: 10px;';
            else {
              if (type == 'warning') div.style = 'background: yellow; padding: 10px;';
              setTimeout(function() {
                warningBanner.removeChild(div);
                updateBannerVisibility();
              }, 5000);
            }
            updateBannerVisibility();
          }
    
          var buildUrl = "Build";
          var loaderUrl = buildUrl + "/{{{ LOADER_FILENAME }}}";
          var config = {
            dataUrl: buildUrl + "/{{{ DATA_FILENAME }}}",
            frameworkUrl: buildUrl + "/{{{ FRAMEWORK_FILENAME }}}",
            codeUrl: buildUrl + "/{{{ CODE_FILENAME }}}",
    #if MEMORY_FILENAME
            memoryUrl: buildUrl + "/{{{ MEMORY_FILENAME }}}",
    #endif
    #if SYMBOLS_FILENAME
            symbolsUrl: buildUrl + "/{{{ SYMBOLS_FILENAME }}}",
    #endif
            streamingAssetsUrl: "StreamingAssets",
            companyName: "{{{ COMPANY_NAME }}}",
            productName: "{{{ PRODUCT_NAME }}}",
            productVersion: "{{{ PRODUCT_VERSION }}}",
            showBanner: unityShowBanner,
          };
    
          if (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) {
            var meta = document.createElement('meta');
            meta.name = 'viewport';
            meta.content = 'width=device-width, height=device-height, initial-scale=1.0, user-scalable=no, shrink-to-fit=yes';
            document.getElementsByTagName('head')[0].appendChild(meta);
            container.className = "unity-mobile";
            canvas.className = "unity-mobile";
            config.devicePixelRatio = 1;
          } else {
            canvas.style.width = "100%";
            canvas.style.height = "100%";
          }
    
          loadingBar.style.display = "block";
    
          var script = document.createElement("script");
          script.src = loaderUrl;
          script.onload = () => {
            createUnityInstance(canvas, config, (progress) => {
              progressBarFull.style.width = 100 * progress + "%";
            }).then((unityInstance) => {
              loadingBar.style.display = "none";
              fullscreenButton.onclick = () => {
                unityInstance.SetFullscreen(1);
              };
            }).catch((message) => {
              alert(message);
            });
          };
    
          document.body.appendChild(script);
        </script>
      </body>
    </html>
    <div id="unity-loading-bar">
      <div id="custom-logo" style="width: 200px; height: 200px; margin: 0 auto; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 20px; display: flex; align-items: center; justify-content: center; color: white; font-size: 48px; font-weight: bold;">
        YOUR LOGO
      </div>
      <div id="custom-progress-container" style="width: 300px; height: 20px; margin: 20px auto; background: rgba(255,255,255,0.2); border-radius: 10px; overflow: hidden;">
        <div id="unity-progress-bar-full" style="width: 0%; height: 100%; background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); transition: width 0.3s ease;"></div>
      </div>
      <div id="loading-text" style="text-align: center; color: white; font-family: Arial; font-size: 18px; margin-top: 10px;">
        Loading...
      </div>
    </div>
    createUnityInstance(canvas, config, (progress) => {
      progressBarFull.style.width = 100 * progress + "%";
      var loadingText = document.querySelector("#loading-text");
      if (loadingText) {
        loadingText.textContent = "Loading... " + Math.round(progress * 100) + "%";
      }
    })
    @keyframes pulse {
      0%, 100% { opacity: 1; }
      50% { opacity: 0.5; }
    }
    
    #unity-logo {
      animation: pulse 2s ease-in-out infinite;
    }
    
    @keyframes slideIn {
      from { transform: translateX(-100%); }
      to { transform: translateX(0); }
    }
    
    #unity-progress-bar-full {
      transition: width 0.3s ease;
      animation: slideIn 0.5s ease-out;
    }
    .catch((message) => {
      // Hide loading screen
      loadingBar.style.display = "none";
      
      // Show custom error UI
      var errorDiv = document.createElement('div');
      errorDiv.style.cssText = 'position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0,0,0,0.9); color: white; padding: 40px; border-radius: 10px; text-align: center; z-index: 1000;';
      errorDiv.innerHTML = '<h2>Failed to Load</h2><p>' + message + '</p><button onclick="location.reload()" style="margin-top: 20px; padding: 10px 20px; background: #667eea; color: white; border: none; border-radius: 5px; cursor: pointer;">Retry</button>';
      document.body.appendChild(errorDiv);
    });
    cd path/to/your/build/folder
    python -m http.server 8000
    #unity-canvas {
      background: #231F20; /* Change to your desired color */
      /* Or use a gradient: */
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    }
    npm install -g http-server
    cd path/to/your/build/folder
    http-server -p 8000
    <div id="splash-screen" style="position: absolute; width: 100%; height: 100%; background: #000; display: flex; align-items: center; justify-content: center; z-index: 1000;">
      <div style="text-align: center; color: white;">
        <h1>Your Game Title</h1>
        <p>Loading...</p>
      </div>
    </div>
    .then((unityInstance) => {
      loadingBar.style.display = "none";
      var splash = document.querySelector("#splash-screen");
      if (splash) splash.style.display = "none";
      // ... rest of code
    })