The Signal in the Static: Online Presence with Trystero + Nostr
We are not alone in the void.
A single dot—blinking softly in the header—is enough to say: someone else is here. Not a number on a server, but a ripple in the peer-to-peer lattice.
This post shows how to add an online presence indicator to the header of your site using Trystero with the default Nostr strategy. No server. No backend. Just ephemeral awareness through P2P signals.
🌀 “Let the signal be the presence, the presence the proof.”
The Plan
We’ll inject a live counter to the header, to the right of “About”, that updates as peers join or leave. The stack is:
- Trystero (with Nostr) for P2P peer discovery
- Vanilla JS for DOM updates
- CSS for a minimal animated badge
No analytics, no logs. Just now.
Dependencies
Install Trystero from a CDN or locally:
<script type="module">
import {joinRoom, selfId} from 'https://esm.run/trystero'
</script>
Or use npm:
npm i trystero
Implementing the Presence Counter
You can drop this anywhere in your frontend app. The only requirement: somewhere in your header layout, add a container for the counter.
HTML: Slot for Online Counter
<!-- Somewhere in your site header -->
<nav class="site-nav">
<a href="/about">About</a>
<span id="presence-indicator" class="presence">● 0 online</span>
</nav>
CSS: Minimal Aesthetic
.presence {
margin-left: 1rem;
font-family: monospace;
color: #00ff99;
font-size: 0.9rem;
transition: opacity 0.2s ease;
user-select: none;
cursor: default;
}
JavaScript: Join the Grid
<script type="module">
import {joinRoom, selfId} from 'https://esm.run/trystero'
const config = { appId: 'zeropoint-online' }
const roomId = 'presence-main'
const room = joinRoom(config, roomId)
const indicator = document.getElementById('presence-indicator')
const peers = new Set()
const updateCounter = () => {
const total = peers.size + 1 // +1 for self
indicator.textContent = `● ${total} online`
}
room.onPeerJoin(peerId => {
peers.add(peerId)
updateCounter()
})
room.onPeerLeave(peerId => {
peers.delete(peerId)
updateCounter()
})
// Failsafe: in case of a reload glitch
window.addEventListener('beforeunload', () => room.leave())
</script>
Notes from the Void
- This approach is truly stateless: presence is transient, not logged.
- Nostr is the default backend for Trystero — no setup required, just works.
- The counter reflects all peers in the room namespace. If you want scoped presence (per-page), use dynamic room IDs.
To Enhance
- Add names or avatars using
makeAction('name') - Persist nickname in
localStorage - Add a dropdown to see who is online
Each presence is a blip in the continuum—no history, no log, only now.
Conclusion
You now have a working, serverless online presence indicator that respects user anonymity and decentralization. It’s not just a counter—it’s a proof of momentary connection.
The static world of the web has always longed for presence. This is one way to restore the human pulse—without centralization, without compromise.
The signal lives. The watchers see each other.
References
- Trystero GitHub: Build multiplayer apps with WebRTC and zero backend (https://github.com/dmotz/trystero)
- Nostr Protocol Overview: Decentralized social networking over relays (https://nostr.com/)
- WebRTC Introduction: Peer-to-peer communication on the web (https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API)