Skip to content

Full experience: Vue + Three.js + Monolook

Step-by-step guide for creating a complete AR experience, based on the Springs of Love (Brussels) project.

Stack

PackageUsage
Vue 3UI framework (Composition API)
Vue RouterNavigation between screens
PiniaGlobal state
Three.jsWebGL 3D engine
Anime.jsUI animations
Vue3-LottieLottie animations (loading, onboarding)
BowserPlatform detection
ViteBuild tool

Scaffolding

bash
npm create vite@latest my-experience -- --template vue
cd my-experience
npm install
npm install vue-router@4 pinia three animejs vue3-lottie bowser

Project structure

src/
├── App.vue
├── main.js
├── router/
│   └── index.js
├── stores/
│   └── experience.js
├── views/
│   ├── StartView.vue          # Start screen
│   └── ExperienceView.vue     # AR view
├── components/
│   ├── ThreeScene.vue         # 3D scene
│   ├── Header.vue
│   ├── Sidebar.vue
│   └── HotspotCard.vue
├── composables/
│   └── useMonolook.js         # SDK logic
└── assets/
    ├── models/                # GLB, GLTF
    ├── textures/
    └── audio/

Experience flow

[StartView] → click → [ExperienceView]

                    [Detect platform]
                      ↙           ↘
              [WebXR]           [AppClip]
              (Android/Quest)    (iOS)
                      ↘           ↙
                    [ThreeScene]

                  [3D content + UI]

Vite configuration

javascript
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  server: {
    https: true  // WebXR requires HTTPS
  },
  build: {
    target: 'esnext'
  }
})

HTTPS required

WebXR requires HTTPS. Always configure server.https: true in development.

Entry point

javascript
// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import router from './router'
import App from './App.vue'

createApp(App)
  .use(createPinia())
  .use(router)
  .mount('#app')

Routing system

javascript
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import StartView from '../views/StartView.vue'
import ExperienceView from '../views/ExperienceView.vue'

export default createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/', component: StartView },
    { path: '/experience', component: ExperienceView }
  ]
})

Platform detection

javascript
import Bowser from 'bowser'

const browser = Bowser.getParser(navigator.userAgent)
const os = browser.getOSName()
const isIOS = os === 'iOS'
const isAndroid = os === 'Android'
const isDesktop = !isIOS && !isAndroid

Next step

This guide covers the base structure. For more detail on each component: