Hey everyone π,
Today I want to share something fun. Imagine this: you're at home with friends, the big TV is on, and instead of everyone fighting over Bluetooth controllers or passing around one gamepad⦠everyone just pulls out their phone and instantly becomes a player.
No accounts. No installs. Just scan a QR code and start mashing buttons.
That's exactly what TouchCoop enables β a tiny TypeScript library that turns this vision into reality with almost zero server hassle.
- Host runs the game on a laptop/TV-connected browser
- Up to 4 players join via QR code on their mobiles
- Touch buttons on phone β real-time input to the game via WebRTC
- Perfect for casual games: platformers, party games, local puzzles
- Not suited for low-latency games like FPS (WebRTC latency is good but not esports-level)
Quick architecture overview
Your game needs two distinct entry points (URLs):
The Match page (TV/Laptop): Creates a
Matchinstance, shows QR codes for joining, and receives player events.The Gamepad page (Phone): Opened via QR code, creates a
Playerinstance, connects, and sends touch events.
Both are just static web pages β no backend required beyond the signaling phase.
Getting started in 60 seconds
Install the library:
npm install touch-coop
1. The Match side (your game)
import { Match, PlayerEvent } from "touch-coop";
const gamePadURL = "https://your-domain.com/gamepad"; // Must be absolute URL for QR
function handlePlayerEvent(event: PlayerEvent) {
switch (event.action) {
case "JOIN":
console.log(`Player ${event.playerId} joined π`);
// Maybe spawn player avatar, play sound, etc.
break;
case "LEAVE":
console.log(`Player ${event.playerId} left π’`);
break;
case "MOVE":
console.log(`Player ${event.playerId} β ${event.button}`);
// Here you map "up", "A", "X" etc. to game actions
if (event.button === "up") jumpPlayer(event.playerId);
break;
}
}
const match = new Match(gamePadURL, handlePlayerEvent);
const { dataUrl } = await match.createLobby(
gamePadURL, handlePlayerEvent
);
Call match.createLobby() to get the dataUrl of a QR Code that displays a virtual gamepad.
2. The Gamepad side (React example)
import React, { useEffect, useState } from "react";
import { Player } from "touch-coop";
const player = new Player(); // Can pass custom PeerJS config too
export default function GamePad() {
const [loading, setLoading] = useState(true);
useEffect(() => {
(async () => {
try {
await player.joinMatch("Player Name Here");
setLoading(false);
} catch (err) {
console.error("Failed to join", err);
}
})();
}, []);
if (loading) return <div className="text-2xl">Connecting...</div>;
return (
<div className="grid grid-cols-3 gap-4 p-8 h-screen bg-black text-white">
<button className="btn" onClick={() => player.sendMove("up")}>β</button>
<button className="btn" onClick={() => player.sendMove("left")}>β</button>
<button className="btn" onClick={() => player.sendMove("right")}>β</button>
<button className="btn col-start-2" onClick={() => player.sendMove("down")}>β</button>
<div className="col-span-3 flex justify-around mt-8">
<button className="btn bg-green-600" onClick={() => player.sendMove("A")}>A</button>
<button className="btn bg-blue-600" onClick={() => player.sendMove("B")}>B</button>
<button className="btn bg-red-600" onClick={() => player.sendMove("X")}>X</button>
<button className="btn bg-yellow-600" onClick={() => player.sendMove("Y")}>Y</button>
</div>
</div>
);
}
Live demo & try it yourself
The original project has a nice little demo:
β https://SlaneyEE.github.io/touch-coop/demos/match.html
Final thoughts
TouchCoop is a beautiful example of how far browser APIs have come: WebRTC + TypeScript + modern build tools = couch co-op without native apps or complex backends.
If you're building casual multiplayer experiences or party games give it a try.
Have you built (or are you planning to build) a couch co-op game? Drop a comment below β I'd love to hear your multiplayer war stories or see links to your projects!
Happy coding, and see you in the comments βοΈ
Top comments (1)
Bro this is so cool for a libaryπ₯π₯