Skip to content

TarunDev1478/MetaPresense

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Meta-Presence

A 2D virtual office that brings in-person presence to remote teams.

Meta-Presence is a real-time collaborative workspace where users join as avatars on a shared canvas, walk around virtual rooms, and have spontaneous conversations the same way you would in a physical office — except the office is on the web. Voice and video activate automatically when avatars come within proximity of each other, recreating the feel of bumping into a colleague at the coffee machine.

Originally built as a virtual classroom and meeting tool, Meta-Presence was adopted at IIITDM Kurnool for online classes and team meetings, replacing traditional video-conferencing for spatial-collaboration use cases.


✨ Features

  • Avatar-based presence — every user is represented by an avatar that moves freely on a shared 2D canvas, rendered via the HTML5 Canvas API for smooth real-time movement
  • Spatial rooms — virtual rooms isolate conversations. Only avatars inside the same room can see each other's chat messages and join its video call.
  • Proximity-triggered WebRTC — peer-to-peer voice and video connections form automatically when avatars come close, and dissolve as they move apart
  • Real-time movement sync — avatar positions broadcast over WebSockets with low end-to-end latency
  • Interest management — each user only receives position updates from nearby peers, keeping large rooms performant
  • Google sign-in — authenticated sessions via Google OAuth for friction-free login
  • Type-safe everywhere — TypeScript across all apps and packages, with Zod schema validation at the network boundary

🎯 Use Cases

  • Online classes — students move between virtual classrooms; group breakouts happen by walking together to a corner of the canvas
  • Distributed team standups — replicate the "everyone gathers around" feeling instead of a grid of disconnected faces
  • Hackathons and study groups — spontaneous side-conversations form by walking near someone, just like the real world
  • Office hours and consultations — instructors hold open rooms; students "walk in" when they have a question

🏗️ How It Works

Architecture overview

┌─────────────────┐         ┌─────────────────┐
│   Browser A     │         │   Browser B     │
│  (Canvas + TS)  │         │  (Canvas + TS)  │
└────────┬────────┘         └────────┬────────┘
         │                           │
         │  WebSocket                │  WebSocket
         │  (movement, chat,         │
         │   signaling)              │
         │                           │
         └──────────┬────────────────┘
                    │
            ┌───────▼────────┐
            │  Node Server   │
            │  (WebSocket    │
            │   broadcast +  │
            │   WebRTC       │
            │   signaling)   │
            └────────────────┘

         ┌─────────────────────────┐
         │   Direct P2P (WebRTC)   │
         │   when avatars are      │
         │   within proximity      │
         └─────────────────────────┘
            Browser A ◄────► Browser B

The server's only job is signaling and broadcasting. Once two avatars are close enough to talk, they negotiate a peer-to-peer WebRTC connection and the video/audio stream never touches the server.

The proximity trigger

Every avatar has a proximity radius. When two avatars' radii overlap, the system fires a join_proximity event between them and they begin a WebRTC negotiation. When they move apart and the radii no longer overlap, a leave_proximity event tears down the connection.

The hard part of this isn't the WebRTC — it's tuning the radius and the activation timing:

  • Too eager: voice connects the moment you're slightly close — feels invasive
  • Too lazy: you're standing right next to someone and nothing happens — feels broken
  • The sweet spot: activation feels natural; you walk up, the call connects within ~300ms, the audio fades in smoothly

Getting this right took multiple iterations against real user feedback.

Interest management (spatial culling)

The naive approach to a multiplayer canvas is to broadcast every user's position to every other user on every tick. With N users, that's O(N²) messages per tick. With 50 students in a virtual classroom, that's 2,500 movement messages every frame — your network and your WebSocket layer both die.

Meta-Presence uses interest management: each user is only notified about avatars within their area of interest (a region around their own position). The server filters movement updates by spatial relevance before forwarding them.

This pulls per-tick fan-out from O(N²) toward O(N) (each user receives roughly a constant number of nearby-peer updates), making the system work smoothly with dozens of concurrent users in the same room.

Room isolation

Rooms aren't just visual — they're scoped at the data layer:

  • Chat messages are tagged with room_id and only delivered to users currently inside that room
  • WebRTC signaling is gated by room — you can't initiate a peer connection with someone outside your room
  • Movement updates still broadcast within your interest area, so you can see who's near a room's door without joining

This means a private meeting in Room A is genuinely private — Room B users have no signal channel into it.

Why Canvas, not DOM?

Avatar rendering uses the HTML5 Canvas API rather than positioning DOM elements. For real-time multi-avatar movement with 30+ users on screen, the DOM is too slow — every position update would trigger layout recalculation. Canvas batches all avatar drawing into a single render call per frame, which is the standard approach for any real-time multiplayer canvas.


🛠️ Tech Stack

Layer Technology
Language TypeScript 5.5
Monorepo Turborepo + npm workspaces
Rendering HTML5 Canvas API
Real-time client Browser WebSocket API, WebRTC API
Backend Node.js (≥18)
Real-time transport WebSocket (signaling + broadcast)
Media transport WebRTC (peer-to-peer voice/video)
Authentication Google OAuth (google-auth-library)
Schema validation Zod
Code formatting Prettier

📂 Project Structure

Meta-Presence is organized as a Turborepo monorepo with multiple apps and shared packages:

MetaPresense/
├── Metaverse/                    # Monorepo root
│   ├── apps/                     # Deployable applications
│   │   └── ...                   # Web client, signaling server, etc.
│   ├── packages/                 # Shared internal packages
│   │   └── ...                   # Shared types, schemas, utilities
│   ├── package.json              # Workspace root config
│   ├── turbo.json                # Turborepo pipeline config
│   └── tsconfig.json             # Base TypeScript config
├── tests/                        # Test suite
├── .gitattributes
├── .gitignore
├── package.json                  # Top-level workspace manifest
└── README.md

Why a monorepo?

  • Shared types between client and server — schemas defined once with Zod, validated on both sides
  • Coordinated builds — Turborepo caches and orchestrates so unchanged packages don't rebuild
  • Single dependency tree — npm workspaces hoists shared deps to the root

🚀 Quick Start

Prerequisites

  • Node.js ≥ 18 (the version this project's engines field requires)
  • npm ≥ 10.2.5 (the version locked via packageManager)
  • A modern browser (Chrome, Edge, or Firefox — WebRTC support required)
  • Camera and microphone access (for voice/video features)
  • A Google OAuth client ID (for sign-in — see Configuration below)

1. Clone the repository

git clone https://github.com/TarunDev1478/MetaPresense.git
cd MetaPresense

2. Install dependencies

npm install at the root will install dependencies for all workspaces (apps and packages):

npm install

3. Configure environment

Each app inside apps/ has its own .env requirements. At minimum you'll need a Google OAuth client ID configured (see Configuration section). Check each app's directory for an .env.example if present.

4. Start development

Run all apps in development mode simultaneously:

npm run dev

Turborepo orchestrates this — it runs the dev script in every workspace that has one (frontend dev server, backend signaling server, etc.) in parallel, with live reload across all of them.

The web client will typically be available at http://localhost:3000 (check your client app's config to confirm the exact port).

5. Open in multiple browser tabs

To see two avatars interact, open the URL in two different browser windows (or one normal + one incognito). Sign in with different Google accounts in each, walk the avatars toward each other, and watch the WebRTC call activate.


🧪 Other Commands

# Build all apps and packages in dependency order
npm run build

# Lint all workspaces
npm run lint

# Format all TypeScript, TSX, and Markdown files with Prettier
npm run format

Turborepo caches build outputs, so subsequent builds of unchanged code are nearly instant.


🔧 Configuration

Google OAuth setup

Meta-Presence uses Google OAuth for sign-in. To run locally:

  1. Go to the Google Cloud Console
  2. Create a new project (or use an existing one)
  3. Enable the Google Identity Services API
  4. Create an OAuth 2.0 Client ID for a Web application
  5. Add http://localhost:3000 (or your dev port) to Authorized JavaScript origins and Authorized redirect URIs
  6. Copy the Client ID into the relevant app's .env file (typically GOOGLE_CLIENT_ID=...)

For production, repeat the same setup with your deployed domain added to the authorized origins.


🌐 Deployment

Meta-Presence needs hosts that support persistent WebSocket connections. Serverless platforms (Vercel functions, AWS Lambda) won't work for the signaling backend — pick one of:

  • Render — free tier supports WebSockets, easy Node.js deploys
  • Railway — clean Node.js deployment experience with WebSocket support
  • Fly.io — global edge deployment, WebSocket-friendly
  • A VPS (DigitalOcean, AWS EC2) — most control, requires nginx reverse-proxy config with Upgrade header forwarding

For the frontend, you can deploy to Vercel or Netlify even though the backend lives elsewhere — Turborepo's monorepo structure plays well with Vercel's per-app deployment.

WebRTC across networks

For peer-to-peer connections to work for users behind NATs, you'll need a STUN/TURN server. Public STUN servers (e.g., Google's stun:stun.l.google.com:19302) work for most home networks. For users behind strict corporate firewalls, you'll need a TURN server too — coturn is the standard open-source option.


🗺️ Roadmap

  • Screen sharing inside proximity calls
  • Persistent room customization (admins can decorate rooms)
  • Recording for class playback
  • Mobile-responsive avatar controls
  • Spatial audio (volume falls off with avatar distance)
  • Integration with calendar tools for scheduled rooms

🙌 Acknowledgments

  • Built at IIITDM Kurnool as a side project during the 2025 academic year
  • Inspired by spatial-collaboration tools like Gather and SpatialChat
  • Real-user feedback from classmates and faculty during early adoption shaped most of the polish

📫 Contact

Built by Tarun Kumar

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors