diff --git a/avatar-swap/.dclignore b/avatar-swap/.dclignore
new file mode 100644
index 000000000..296236bd5
--- /dev/null
+++ b/avatar-swap/.dclignore
@@ -0,0 +1,14 @@
+.*
+package.json
+package-lock.json
+yarn-lock.json
+build.json
+export
+tsconfig.json
+tslint.json
+node_modules
+*.ts
+*.tsx
+Dockerfile
+dist
+screenshots
\ No newline at end of file
diff --git a/avatar-swap/.eslintrc.json b/avatar-swap/.eslintrc.json
new file mode 100644
index 000000000..91cac6e12
--- /dev/null
+++ b/avatar-swap/.eslintrc.json
@@ -0,0 +1,6 @@
+{
+ "extends": "@dcl/eslint-config/sdk",
+ "parserOptions": {
+ "project": ["tsconfig.json"]
+ }
+}
diff --git a/avatar-swap/.gitignore b/avatar-swap/.gitignore
new file mode 100644
index 000000000..2c2fba0f8
--- /dev/null
+++ b/avatar-swap/.gitignore
@@ -0,0 +1,7 @@
+package-lock.json
+node_modules
+bin
+*.swp
+*.*~
+export
+.idea/
diff --git a/avatar-swap/README.md b/avatar-swap/README.md
new file mode 100644
index 000000000..7b035e8b9
--- /dev/null
+++ b/avatar-swap/README.md
@@ -0,0 +1,40 @@
+# Avatar Swap
+A scene that uses `AvatarModifier` component to swap out the default avatar for another character model. This way the player's character style can match that of the enviroment's.
+
+![](screenshots/avatar-swap.gif)
+
+This scene shows:
+- How to add a 3D model
+- How to play animations from a 3D model
+- How to hide an avatar using `AvatarModifier` component
+- How to attach an entity to the Player
+- How to get the Player's entity `Transform` and its position
+
+## Instructions
+Run over to the area covered in grass to automatically switch avatars. Use your mouse to look around and W A S D keys on your keyboard to move forward, left, backward and right respectively.
+
+## Try it out
+
+**Install the CLI**
+
+Download and install the Decentraland CLI by running the following command inside this scene root directory:
+
+```bash
+npm install @dcl/sdk@next
+```
+
+**Previewing the scene**
+
+Inside this scene root directory run:
+
+```
+dcl start
+```
+
+## Copyright info
+
+This scene is protected with a standard Apache 2 licence. See the terms and conditions in the [LICENSE](/LICENSE) file.
+
+## Acknowledgements
+
+Model and animations from https://www.mixamo.com/
diff --git a/avatar-swap/models/arissa.glb b/avatar-swap/models/arissa.glb
new file mode 100644
index 000000000..cd620db34
Binary files /dev/null and b/avatar-swap/models/arissa.glb differ
diff --git a/avatar-swap/models/baseGrass.glb b/avatar-swap/models/baseGrass.glb
new file mode 100644
index 000000000..baa9f03d5
Binary files /dev/null and b/avatar-swap/models/baseGrass.glb differ
diff --git a/avatar-swap/package.json b/avatar-swap/package.json
new file mode 100644
index 000000000..b837aeace
--- /dev/null
+++ b/avatar-swap/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "avatar-swap",
+ "version": "1.0.0",
+ "description": "Avatar Swap",
+ "scripts": {
+ "start": "dcl start",
+ "build": "build-ecs",
+ "watch": "build-ecs --watch",
+ "lint": "eslint . --ext .ts",
+ "lint:fix": "eslint . --ext .ts --fix"
+ },
+ "devDependencies": {
+ "@dcl/eslint-config": "^1.0.1",
+ "@dcl/sdk": "next"
+ }
+}
diff --git a/avatar-swap/scene.json b/avatar-swap/scene.json
new file mode 100644
index 000000000..3193d426d
--- /dev/null
+++ b/avatar-swap/scene.json
@@ -0,0 +1,37 @@
+{
+ "ecs7": true,
+ "display": {
+ "title": "avatar-swap",
+ "favicon": "favicon_asset"
+ },
+ "contact": {
+ "name": "decentraland",
+ "email": ""
+ },
+ "owner": "",
+ "scene": {
+ "parcels": [
+ "23,23"
+ ],
+ "base": "23,23"
+ },
+ "requiredPermissions": [],
+ "main": "bin/game.js",
+ "tags": [],
+ "spawnPoints": [
+ {
+ "name": "spawn1",
+ "default": true,
+ "position": {
+ "x": 0,
+ "y": 0,
+ "z": 0
+ },
+ "cameraTarget": {
+ "x": 8,
+ "y": 1,
+ "z": 8
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/avatar-swap/screenshots/avatar-swap.gif b/avatar-swap/screenshots/avatar-swap.gif
new file mode 100644
index 000000000..b4f29afdc
Binary files /dev/null and b/avatar-swap/screenshots/avatar-swap.gif differ
diff --git a/avatar-swap/src/index.ts b/avatar-swap/src/index.ts
new file mode 100644
index 000000000..85fe34ef5
--- /dev/null
+++ b/avatar-swap/src/index.ts
@@ -0,0 +1,28 @@
+export * from '@dcl/sdk'
+import { engine, GltfContainer, Transform } from '@dcl/sdk/ecs'
+import { Vector3 } from '@dcl/sdk/math'
+import { attachEntityToPlayer } from "./modules/utils";
+import { createArissaCharacter } from "./modules/arissa";
+import { createAvatarSwappingArea, avatarSwappingSystem } from "./modules/avatarSwappingArea";
+
+function setup() {
+ // Instantiate ground model
+ const groundEntity = engine.addEntity()
+ GltfContainer.create(groundEntity, {
+ src: "models/baseGrass.glb"
+ })
+
+ // Instantiate 'Arissa' character animated model
+ const arissaCharaEntity = createArissaCharacter()
+ attachEntityToPlayer(Transform.get(arissaCharaEntity).parent)
+
+ // Set avatar modifier area to swap player avatar
+ createAvatarSwappingArea(Vector3.create(8, 2, 10.5), Vector3.create(16, 4, 11), arissaCharaEntity)
+
+ // Register avatar swapping system
+ engine.addSystem(avatarSwappingSystem)
+}
+
+setup()
+
+
diff --git a/avatar-swap/src/modules/arissa.ts b/avatar-swap/src/modules/arissa.ts
new file mode 100644
index 000000000..17565aa02
--- /dev/null
+++ b/avatar-swap/src/modules/arissa.ts
@@ -0,0 +1,38 @@
+import {
+ engine,
+ Entity,
+ GltfContainer,
+ Transform,
+ Animator
+} from '@dcl/sdk/ecs'
+import { Vector3 } from "@dcl/sdk/math";
+
+export function createArissaCharacter() : Entity {
+ const parentEntity = engine.addEntity()
+ const entity = engine.addEntity()
+
+ GltfContainer.create(entity, {
+ src: "models/arissa.glb"
+ })
+ Transform.create(entity, {
+ position: Vector3.create(0, 1.75, 0),
+ scale: Vector3.create(0, 0, 0),
+ parent: parentEntity
+ })
+ Animator.create(entity, {
+ states: [
+ {
+ name: "Running",
+ clip: "Running",
+ loop: true
+ },
+ {
+ name: "Idle",
+ clip: "Idle",
+ loop: true
+ }
+ ]
+ })
+
+ return entity
+}
\ No newline at end of file
diff --git a/avatar-swap/src/modules/avatarSwappingArea.ts b/avatar-swap/src/modules/avatarSwappingArea.ts
new file mode 100644
index 000000000..ed2c6bcb0
--- /dev/null
+++ b/avatar-swap/src/modules/avatarSwappingArea.ts
@@ -0,0 +1,76 @@
+import {
+ engine,
+ Entity,
+ Transform,
+ Animator,
+ AvatarModifierArea,
+ AvatarModifierType
+} from '@dcl/sdk/ecs'
+import { Vector3 } from '@dcl/sdk/math'
+
+let areaCenter: Vector3
+let areaSize: Vector3
+let areaMinPosition: Vector3
+let areaMaxPosition: Vector3
+
+function setupAreaData(center: Vector3, size: Vector3) {
+ areaCenter = center
+ areaSize = size
+
+ const halfSize = Vector3.scale(size, 0.5)
+ areaMinPosition = Vector3.create(
+ areaCenter.x - halfSize.x,
+ areaCenter.y - halfSize.y,
+ areaCenter.z - halfSize.z
+ )
+ areaMaxPosition = Vector3.create(
+ areaCenter.x + halfSize.x,
+ areaCenter.y + halfSize.y,
+ areaCenter.z + halfSize.z
+ )
+}
+
+function isPositionInsideArea(targetPosition: Vector3): boolean {
+ return targetPosition.x > areaMinPosition.x
+ && targetPosition.y > areaMinPosition.y
+ && targetPosition.z > areaMinPosition.z
+ && targetPosition.x < areaMaxPosition.x
+ && targetPosition.y < areaMaxPosition.y
+ && targetPosition.z < areaMaxPosition.z
+}
+
+let otherAvatarEntity: Entity
+export function createAvatarSwappingArea(center: Vector3, size: Vector3, avatarEntity: Entity) {
+ setupAreaData(center, size)
+ otherAvatarEntity = avatarEntity
+ const avatarHiderAreaEntity = engine.addEntity()
+ AvatarModifierArea.create(avatarHiderAreaEntity, {
+ area: areaSize,
+ modifiers: [AvatarModifierType.AMT_HIDE_AVATARS],
+ excludeIds: []
+ })
+ Transform.create(avatarHiderAreaEntity, {
+ position: areaCenter
+ })
+}
+
+let lastPlayerPos: Vector3 | undefined = undefined
+export function avatarSwappingSystem (dt: number) {
+ if (!Transform.has(engine.PlayerEntity)) return
+
+ const playerPos = Transform.get(engine.PlayerEntity).position
+ const moved = playerPos != lastPlayerPos
+
+ Animator.getClip(otherAvatarEntity, "Idle").playing = !moved
+ Animator.getClip(otherAvatarEntity, "Running").playing = moved
+
+ if (!moved) return
+
+ const playerIsInsideHidingArea = isPositionInsideArea(playerPos)
+ const otherAvatarTransform = Transform.getMutable(otherAvatarEntity)
+ otherAvatarTransform.scale.x = playerIsInsideHidingArea ? 1.1 : 0
+ otherAvatarTransform.scale.y = playerIsInsideHidingArea ? 1.1 : 0
+ otherAvatarTransform.scale.z = playerIsInsideHidingArea ? 1.1 : 0
+
+ lastPlayerPos = playerPos
+}
\ No newline at end of file
diff --git a/avatar-swap/src/modules/utils.ts b/avatar-swap/src/modules/utils.ts
new file mode 100644
index 000000000..6a7fe1252
--- /dev/null
+++ b/avatar-swap/src/modules/utils.ts
@@ -0,0 +1,13 @@
+import { AvatarAnchorPointType, AvatarAttach, Entity } from "@dcl/sdk/ecs";
+import { getUserData } from "~system/UserIdentity"
+
+export async function attachEntityToPlayer (entity: Entity){
+ let userData = await getUserData({})
+ if(!userData.data) return
+ console.log(`userId: ${userData.data.userId}`)
+
+ AvatarAttach.create(entity, {
+ anchorPointId: AvatarAnchorPointType.AAPT_POSITION,
+ avatarId: userData.data.userId
+ })
+}
\ No newline at end of file
diff --git a/avatar-swap/tsconfig.json b/avatar-swap/tsconfig.json
new file mode 100644
index 000000000..91f9d6d06
--- /dev/null
+++ b/avatar-swap/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "compilerOptions": {},
+ "include": ["src/**/*.ts", "@dcl/ecs"],
+ "extends": "@dcl/sdk/types/tsconfig.ecs7.strict.json"
+}