Event listeners

There are several events that the scene can subscribe to, to know the actions of the player while in or near the scene.

For button and click events performed by the player, see Button events.

Player connects or disconnects

Whenever another player starts or stops being rendered by the local engine, this creates an event you can listen to. Players may or may not be standing on the same scene as you, but must be within visual range (not necessarily in sight). The onPlayerConnectedObservable detects both when a player newly connects nearby or comes close enough to be in visual range, likewise the onPlayerDisconnectedObservable detects when a player ends their session or or walks far away.

onPlayerConnectedObservable.add((player) => {
  log("player entered: ", player.userId)
})

onPlayerDisconnectedObservable.add((player) => {
  log("player left: ", player.userId)
})

Keep in mind that if other players are already being rendered in the surroundings before the player has loaded your scene, this event won’t notify the newly loaded scene of the already existing players. If you need to keep track of all current players, you can query for existing players upon scene loading, and then listen to this event for updates.

getConnectedPlayers().then((players) => {
  players.forEach((player) => {
    log("player was already here: ", player.userId)
  })
})

Player enters or leaves scene

Whenever an avatar steps inside or out of the parcels of land that make up your scene, or teleports in or out, this creates an event you can listen to. This event is triggered by all avatars, including the player’s.

onEnterSceneObservable.add((player) => {
  log("player entered scene: ", player.userId)
})

onLeaveSceneObservable.add((player) => {
  log("player left scene: ", player.userId)
})

Note: This event only responds to players that are currently being rendered locally. In large scenes where the scene size exceeds the visual range, players entering in the opposite corner may not be registered. If the number of players in the region exceeds the capabilities of an island on Decentraland servers, players that are not sharing a same island aren’t visible and are not tracked by these events either.

Only current player

You can filter out the triggered events to only react to the player’s avatar, rather than other avatars that may be around.

getUserData().then((myPlayer) => {
  onEnterSceneObservable.add((player) => {
    log("player entered scene: ", player.userId)
    if (player.userId === myPlayer?.userId) {
      log("I entered the scene!")
    }
  })

  onLeaveSceneObservable.add((player) => {
    log("player left scene: ", player.userId)
    if (player.userId === myPlayer?.userId) {
      log("I left the scene!")
    }
  })
})

This example first obtains the player’s id, then subscribes to the events and compares the userId returned by the event to that of the player.

Query all players in scene

You can also get the full list of players who are currently on your scene and being rendered by calling getPlayersInScene().

getPlayersInScene().then((players) => {
  players.forEach((player) => {
    log("player was already here: ", player.userId)
  })
})

Player changes camera mode

When the player changes the camera mode between 1st and 3rd person in or near your scene, this creates an event you can listen to.

onCameraModeChangedObservable.add(({ cameraMode }) => {
  log("Camera mode changed:", cameraMode)
})

The value of the returned property can either be CameraMode.FirstPerson or CameraMode.ThirdPerson.

This event informs changes to the camera mode once the player is already in or near your scene. To know the initial camera mode of the player when first arriving to your scene, check the value of Camera.instance.cameraMode. See User Data.

// intitial camera mode
log("Camera mode: ", Camera.instance.cameraMode)

// check for changes
onCameraModeChangedObservable.add(({ cameraMode }) => {
  log("Camera mode changed:", cameraMode)
})

Player plays animation

Whenever the player plays an emote (dance, clap, wave, etc), you can detect this event.

onPlayerExpressionObservable.add(({ expressionId }) => {
  log("Expression: ", expressionId)
})

The event includes the following information:

  • expressionId: Name of the emote performed (ie: wave, clap, kiss)

Note: This event is triggered any time the player makes an emote and the scene is loaded. The player could be standing in a nearby scene when this happens.

Player locks/unlocks cursor

Players can switch between two cursor modes: locked cursor mode to control the camera or unlocked cursor mode for moving the cursor freely over the UI.

With a keyboard, players unlock the cursor by pressing the Esc key, and lock the cursor back by clicking anywhere in the screen.

This onPointerLockedStateChange event is activated each time a player switches between these two modes, while near the scene.

onPointerLockedStateChange.add(({ locked }) => {
  if (locked) {
    log("Pointer has been locked")
  } else {
    log("Pointer has been unlocked")
  }
})

Note: This event is triggered even if the player is not standing directly inside the scene.

The locked property from this event is a boolean value that is true when the player locks the cursor and false when the player unlocks the cursor.

This event is useful if the player needs to change cursor modes and may need a hint for how to lock/unlock the cursor.

This can also be used in scenes where the player is expected to react fast, but the action can take a break while the player has the cursor unlocked.

Player goes idle

Whenever the player is inactive for a full minute, without interacting with any input being picked up by the Decentraland explorer, we can consider the player to be idle. Whenever this happens, it creates an event that you can listen to.

onIdleStateChangedObservable.add(({ isIdle }) => {
  log("Idle State change: ", isIdle)
})

The isIdle property is a boolean value that is true when the player enters the idle mode and false when the player leaves the idle mode.

This event is especially useful for multiplayer scenes, when you might want to disconnect from the server players who are likely away from the machine or left Decentraland in a tab in the background.

Note: The idle state is inferred based on the player not using the keyboard or mouse for a full minute. This can of course produce false positives, for example a player might be watching other players interact or watching a video stream, standing still but fully engaged. Be mindful of these corner cases and what the experience is like for a player who stands still for a while.

Player changes profile

Whenever the player makes a change to their profile, the onOwnProfileDataChange event is called. These changes may include putting on different wearables, changing name, description, activating portable experiences, etc.

onOwnProfileDataChange.add((profileData) => {
  log("Own profile data is ", profileData)
})

Event data includes only the ID of the player and a version number for that avatar’s profile, according to the catalyst server. Every time a change is propagated, the version number increases by 1.

Tip: When this event is triggered, you can then use the getUserData() function to fetch the latest version of this information, including the list of wearables that the player has on. You may need to add a slight delay before you call getUserData() to ensure that the version this function returns is up to date.

When testing in preview, run the scene with dcl start --web3 so that you connect with your wallet. Otherwise, you will be using a random avatar.

Note: This event is only triggered by changes to the current player, not by changes on the profiles of other nearby players.

Scene finished loading

When the scene finishes loading, the onSceneReadyObservable gets called. This works both if the player loads straight into the scene, or if the player walks up to the scene from somewhere else. When all of the content in the scene has finished its initial load, including heavy models, etc, this event is called.

onSceneReadyObservable.add(() => {
  log("SCENE LOADED")
})

Video playing

When a VideoTexture changes its playing status, the onVideoEvent observable receives an event.

onVideoEvent.add((data) => {
  log("New Video Event ", data)
})

The input of a video event contains the following properties:

  • videoClipId ( string): The ID for the VideoTexture component that changed status.
  • componentId (string): The ID of the VideoTexture component that changed status.
  • currentOffset (number): The current value of the seek property on the video. This value shows seconds after the video’s original beginning. -1 by default.
  • totalVideoLength (number ): The length in seconds of the entire video. -1 if length is unknown.
  • videoStatus: The value for the new video status of the VideoTexture, expressed as a value from the VideoStatus enum. This enum can hold the following possible values:

  • VideoStatus.NONE = 0,
  • VideoStatus.ERROR = 1,
  • VideoStatus.LOADING = 2,
  • VideoStatus.READY = 3,
  • VideoStatus.PLAYING = 4,
  • VideoStatus.BUFFERING = 5

Learn more about playing videos in Decentraland in Video Playing.

Player changes realm or island

Players in decentraland exist in separate realms, and in separate islands within each realm. Players in different realms or islands cant see each other, interact or chat with each other, even if they’re standing on the same parcels.

Each time the player changes realms or island, the onRealmChangedObservable event gets called.

onRealmChangedObservable.add((realmChange) => {
  log("PLAYER CHANGED ISLAND TO ", realmChange.room)
})

This event includes the following fields:

  • serverName: string; The catalyst server name.
  • room: string; The island name.
  • displayName: string; The catalyst server name followed by a - and the island name. For example unicorn-x011.
  • domain: string; The url to the catalyst server being used.

As players move through the map, they may switch islands to be grouped with those players who are now closest to them. Islands also shift their borders dynamically to fit a manageable group of people in each. So even if a player stands still they could be changed island as others enter and leave surrounding scenes.

If your scene relies on a 3rd party server to sync changes between players in real time, then you may want to only share data between players that are grouped in a same realm+island, so it’s a good practice to change rooms in the 3rd party server whenever players change island.

Player starts/ends the tutorial

When a new player first enters Decentraland for the fist time, they go through a brief tutorial that shows the basic movements and UI elements. Typically players will experience this on Genesis Plaza, but a new player that enters a specific scene from an event as their first time in Decentraland will experience a shortened version of that tutorial wherever they are.

This tutorial includes some music, that could clash with the music of the scene that the player is currently on, so it’s recommended to stop any background music in case the player is going through the tutorial.

import { tutorialEnableObservable } from "src/modules/tutorialHandler"

tutorialEnableObservable.add((tutorialEnabled) => {
  if (tutorialEnabled) {
    log("Player started tutorial")
    backgroundMusicSource.playing = false
  } else {
    log("Player finished tutorial")
    backgroundMusicSource.playing = true
  }
})

Note: This event only occurs when the player starts or ends the tutorial. It doesn’t get called at all in case the player has already experienced the tutorial in a prior session or scene.