Project 05 / 05

VOIP CLIENT

Role
Solo · Design & FE
Year
2021–present
VOIP CLIENT interface
Overview

VoIP and messaging on the web. No native app required.

This is a personal project: a web-native VoIP and messaging platform that does what native apps do, entirely in the browser. SIP-based calling, real-time messaging, and contact management, built from scratch as a solo design and engineering exercise.

Next.jsTypeScriptSIP.jsWebRTCWebSocketsZustand
Type
Personal project · Solo
Protocol
SIP over WebRTC via SIP.js
Real-time
WebSocket-based messaging + signaling
Call latency
Sub-100ms audio path end-to-end
Status
Active development
The Problem

VoIP on the web is notoriously hostile.

WebRTC complexity

SIP + WebRTC isn't a stack you pick up in an afternoon. ICE negotiation, STUN/TURN servers, codec negotiation, NAT traversal: each layer has its own failure modes. Without proper abstraction, the calling logic bleeds into every part of the application.

Call state reliability

A dropped connection is forgivable. A UI that shows "Connected" when the call has dropped is not. Call state machines in WebRTC apps are inherently async and event-driven, so without explicit state modelling, race conditions produce UI states that don't match reality.

Two real-time systems, one interface

VoIP signaling and chat messaging are fundamentally different real-time transports: SIP over WebSocket vs. a standard WS message channel. The interface needed to present them as a unified experience without the underlying complexity leaking through to the user.

Approach

Abstract the hard parts. Model the state machine. Unify the transport.

01

SIP.js abstraction layer

A custom call manager sits on top of SIP.js, exposing a clean interface to the app: initiate, answer, hold, mute, hangup, with no ICE logic, no codec negotiation, just call state.

Technical note

SIP.js UserAgent configured once at app init; STUN/TURN credentials injected from environment. All SIP events (invite, bye, failed, terminated) mapped to state machine transitions. The UI layer never touches SIP primitives directly. Media constraints negotiated at session start.

02

Explicit call state machine

Call lifecycle modelled as an explicit state machine: Idle → Ringing → Connecting → Connected → Held → Ended. Invalid transitions are rejected; the UI subscribes to state, not events: no ghost states, no flicker.

Technical note

Zustand store per active call; transitions trigger via actions, not scattered event handlers. Local audio mute tracked independently from SIP hold; same UI update, different underlying mechanics. Full call history with duration and timestamps persisted to IndexedDB across sessions.

03

Unified socket layer

SIP signaling and chat messages share a single WebSocket with multiplexed channels; from the client's perspective, one connection handles both, and graceful reconnection covers both transports simultaneously.

Technical note

Custom WS multiplexer with message-type headers for routing. Exponential backoff reconnection with queued message replay on reconnect. In-progress calls survive brief disconnects via SIP re-INVITE. Presence state (online/away/in-call) broadcast over the same channel, with no separate polling endpoint.

Feature 01

VoIP Calling

SIP-based voice calls directly in the browser. Mute, hold, and transfer: the full native-app feature set without an install. Audio waveform visualization, call timer, and instant state feedback. Sub-100ms audio path via optimized ICE/STUN configuration.

SIP.jsWebRTCSTUN/TURN
VoIP Calling
Real-time Messaging
Feature 02

Real-time Messaging

Persistent chat alongside every call. Messages survive call disconnects and sync across sessions. Read receipts, delivery status, and presence indicators, all built on the same WebSocket channel as call signaling, so nothing is ever out of sync.

WebSocketsMultiplexed transportIndexedDB persistence
Feature 03

Contact Management

Contact list with live presence indicators: online, away, in-call. One tap to call or message. Presence broadcast over the unified WebSocket channel, updated in real time without polling. Call history per contact with duration and timestamps.

Presence systemReal-time syncCall history
Contact Management
SIP
Web-native VoIP
100ms
Audio latency
WS for both channels
Solo
Design & engineering