EpiAirConsole is inspired by the AirConsole platform, which allows users to play web-based games where the PC acts as the main screen and mobile devices serve as controllers. The goal is to recreate this experience using a modern web stack, enabling seamless integration of 2D games (built with Phaser.js) and real-time communication between devices via Socket.IO. The project is fully developed in TypeScript for maintainability and scalability.
- PC web page displays the game (main screen)
- Mobile web page acts as a controller (gamepad)
- Real-time communication using Socket.IO
- Modular game integration with Phaser.js (2D games)
- Room system with PIN codes for multi-player sessions
- Player-controller pairing with unique device codes
- Friends system with invitations (see FRIENDS.md)
- All code written in TypeScript
See ROOMS.md for details on the room system and controller pairing.
- Frontend: TypeScript, HTML5, SCSS, Socket.IO-client
- Backend: Node.js, TypeScript, Socket.IO-server
- Games: Phaser.js, TypeScript
- Mobile: TypeScript, HTML5, SCSS, Socket.IO-client
- Monorepo: Shared node_modules at the root, each part (frontend, backend, games, mobile) has its own package.json and tsconfig.json
- Clone the repository:
git clone <repo-url> cd EpiAirConsole
- Install all dependencies (from the root):
This will install all required packages for every part of the project (frontend, backend, games, mobile) using the shared node_modules.
npm install
This repo uses a single shared node_modules at the root. For quick development and testing you can run the backend and the test socket clients from the repo root:
- Start backend in dev (auto-reload):
npm run backend:dev- Start a test frontend socket client (local test runner):
npm run socket:frontend- Start a test mobile socket client (local test runner):
npm run socket:mobileOpen the frontend test page at http://localhost:3000/ (served by the backend) to exercise socket flows.
If you prefer to run parts independently (build/start from each subfolder) you can still do so:
- Start the backend server:
cd backend
npm run build # or npm run dev if available
npm start- Start the frontend:
cd ../frontend
npm run build # or npm run dev if available
npm start- Add and run games:
- Each game should be placed in a subfolder of
games/and built with Phaser.js and TypeScript. - Build and run games as needed (see each game's README for details).
Run the backend from the repository root (this project uses a single node_modules at the root):
- Dev (auto-reload):
npm run backend:dev- Build:
npm run backend:build- Start built version:
npm run backend:startThe backend listens on PORT (default 3000) and exposes a /health route.
The mobile interface now supports automatic URL detection. This allows you to connect from any device without having to modify the .env file each time:
-
Auto-Detection Mode (recommended)
To enable automatic URL detection:
cp backend/.env.example backend/.env # Then edit backend/.env and make sure SERVER_URL is commented out or emptyWith auto-detection enabled:
- The mobile interface will automatically detect all available IP addresses
- A dropdown menu will show all possible connection URLs
- You can easily switch between local IPs and ngrok URLs without editing files
- Connection settings are remembered during your session
-
Manual Configuration (alternative)
If you prefer to force a specific URL:
cp backend/.env.example backend/.env # Then edit backend/.env and set SERVER_URL- To force using a LAN IP (for phones on the same network):
SERVER_URL=http://<YOUR_LAN_IP>:3000 - Or to expose via ngrok (for cross-network / HTTPS access):
SERVER_URL=https://<YOUR-NGROK-SUBDOMAIN>.ngrok-free.dev
- To force using a LAN IP (for phones on the same network):
ngrok allows you to expose your local server to the internet, making it accessible from any device, even those not on your local network.
-
Download the latest version of ngrok:
curl -O https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-linux-amd64.tgz tar xvzf ngrok-v3-stable-linux-amd64.tgz
-
Make ngrok executable and move it to a directory in your PATH (optional):
chmod +x ngrok sudo mv ngrok /usr/local/bin/ # requires root/sudo accessAlternatively, keep it in your project directory and run it with
./ngrok
-
Sign up for a free account at ngrok.com and get your authtoken
-
Authenticate your ngrok client:
./ngrok authtoken YOUR_AUTHTOKEN
Replace
YOUR_AUTHTOKENwith the token from your ngrok dashboard.
-
Start your backend server:
npm run backend:dev
-
Start ngrok (in a new terminal):
./ngrok http 3000
This creates a tunnel to your local server running on port 3000.
-
Note the forwarding URL in the ngrok output:
Forwarding https://<your-subdomain>.ngrok-free.dev -> http://localhost:3000 -
Update your backend/.env file with this URL:
SERVER_URL=https://<your-subdomain>.ngrok-free.dev -
Restart your backend server to apply the new SERVER_URL.
-
Restart the backend to apply the changes:
npm run backend:dev
-
Check that the backend exposes the configuration (it should show a non-empty
serverUrlif you set SERVER_URL):curl -sS http://127.0.0.1:3000/config.json # -> {"serverUrl":"http://<YOUR_LAN_IP>:3000","defaultRoom":"test-room"} -
Open the mobile page on your phone (on the same network):
- URL:
http://<YOUR_LAN_IP>:3000/mobile.html - The "Server URL" field will be prefilled and disabled if
SERVER_URLis set. - Click "Connect" then "Send Message" to send a message to the backend.
- URL:
-
Verify reception on the backend
- Look at the terminal where
npm run backend:devis running — you should see socket connection logs and received events (join, message, etc.).
- Look at the terminal where
-
From your computer:
- Open
http://localhost:3000to access the main frontend - Open
http://localhost:3000/mobile.htmlto test the mobile interface
- Open
-
From other devices on the same network:
-
Use your computer's LAN IP:
http://<YOUR_LAN_IP>:3000 -
For the mobile interface:
http://<YOUR_LAN_IP>:3000/mobile.htmlTo display your computer's LAN IP address, use the following command in a terminal: -
Linux/macOS (show only the main LAN IP):
hostname -I | awk '{print $1}'
or
ip addr show | awk '/inet / && !/127.0.0.1/ {print $2}' | cut -d/ -f1 | head -n1
-
Windows (show only IPv4 addresses):
ipconfig | findstr /R /C:"IPv4"
-
-
From any device on the internet (using ngrok):
- Use the ngrok URL:
https://<your-subdomain>.ngrok-free.dev - For the mobile interface:
https://<your-subdomain>.ngrok-free.dev/mobile.html
- Use the ngrok URL:
- "ngrok version too old" error: Download the latest version as described above
- Authentication failures: Verify your authtoken on the ngrok dashboard
- Connection issues: Check if your backend is actually running on port 3000
- CORS errors: Make sure your backend allows requests from the ngrok domain
Security notes
- Never commit
backend/.env(the repo containsbackend/.env.exampleonly). - Use ngrok or HTTPS for cross-network tests to avoid mixed-content issues in mobile browsers.
- The free version of ngrok assigns random subdomains each time you restart, so you'll need to update SERVER_URL in your .env file.
- All commands should be run from the respective folders in development mode.
- Make sure to use Node.js v18+ and npm v9+ for best compatibility.
- For development, you can use
npm run devif available in each part. - Docker instructions will be added soon for unified launch.
EpiAirConsole now includes a complete game development system using Phaser.js. You can create 2D games that players control from their mobile devices.
-
Build the game system (first time):
cd frontend npm run build:game -
Watch mode for development:
cd frontend npm run watch:game
The game system is organized as follows:
frontend/src/games/BaseGame.ts: Abstract base class for all gamesfrontend/src/games/SimpleGameExample.ts: Example game with moving circlesfrontend/src/games/GameManager.ts: Handles game lifecycle and registrationfrontend/src/RoomGameController.ts: Integrates games into the room page
-
Create a new file in
frontend/src/games/extendingBaseGame -
Implement required methods:
createPhaserConfig(): Configure Phaser game settingshandlePlayerInput(): Process controller inputsonPlayerAdded(): Handle player joinsonPlayerRemoved(): Handle player leaves
-
Register your game in
GameManager.ts:export const AVAILABLE_GAMES = { 'simple-example': SimpleGameExample, 'my-new-game': MyNewGame, // Add here } as const;
-
Rebuild:
npm run build:game
Mobile controllers send these actions:
up,down,left,right: Directional movementsaction: Primary action button
- Players join a room using a PIN code
- Each player connects their mobile device as a controller
- Host clicks "Start Game" when all players are ready
- Game initializes with Phaser canvas
- Players control their characters using mobile buttons
- Game handles inputs in real-time via Socket.IO
If you prefer to run a local Postgres in Docker (recommended to avoid touching the system DB), here's a small set of commands and notes you can copy-paste.
- Start a Postgres container (use host port 5433 if your system Postgres uses 5432):
# remove any previous container with the same name (optional)
docker rm -f epi-postgres || true
# start postgres mapped to host port 5433
docker run --name epi-postgres \
-e POSTGRES_USER=epiuser \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=epiairconsole \
-p 5433:5432 -d postgres:15- Update
backend/.envto point to the Docker instance (example):
DB_HOST=127.0.0.1
DB_PORT=5433
DB_USER=epiuser
DB_PASSWORD=secret
DB_NAME=epiairconsole
JWT_SECRET=un-secret-long
- Restart the backend (it will auto-create the
userstable on first run):
npm run backend:dev- Verify connectivity and tables (from host):
# list tables
PGPASSWORD=secret psql -h 127.0.0.1 -p 5433 -U epiuser -d epiairconsole -c "\dt"Notes
- If port 5432 is free on your machine, you can map to
-p 5432:5432instead. - If you see
Ident authentication failedwhen connecting to a system Postgres, prefer the Docker option or adjust your systempg_hba.conf.