The WavezFM Room Extension API provides a stable, official client-side bridge for browser extensions and user scripts running on WavezFM room pages.
It allows integrations without relying on DOM queries or UI interaction hacks.
- Global access via
window.WavezFM - Current version:
v1 - Designed for extensions, scripts, and integrations
- Works only inside room pages
const api = window.WavezFM;
if (!api || api.version !== '1') {
console.warn('WavezFM bridge unavailable');
}window.WavezFMis only available in room pagesapi.room.getState()returnsnulloutside a room- Always check
api.versionfor compatibility
Retrieve a snapshot of the current room state:
const state = window.WavezFM.room.getState();
if (state) {
console.log(state.room.name);
console.log(state.playback?.title);
console.log(state.votes.woots);
}type WavezRoomState = {
room: {
id: string;
slug: string;
name: string;
isVerified: boolean;
viewerRole: string;
queueLocked: boolean;
activeUsersCount: number;
queueCount: number;
};
currentUser: {
id: string;
username: string;
displayUsername: string;
level: number | null;
currentLevelXp: number | null;
xpRequired: number | null;
totalXp: number | null;
fanCount: number | null;
infiniteLevel: boolean;
} | null;
playback: {
playbackKey: string;
trackId: string;
source: 'youtube' | 'soundcloud';
sourceId: string;
title: string;
artist: string;
thumbnailUrl: string | null;
durationMs: number;
isLive: boolean;
startedAtServerMs: number;
paused: boolean;
djId: string;
djUsername: string;
} | null;
votes: {
trackId: string | null;
woots: number;
mehs: number;
grabs: number;
wootUserIds: string[];
mehUserIds: string[];
grabUserIds: string[];
clientVote: 'woot' | 'meh' | null;
clientGrabbed: boolean;
clientGrabPlaylistId: string | null;
canVote: boolean;
};
queue: {
userIds: string[];
count: number;
isJoined: boolean;
isCurrentDj: boolean;
isLocked: boolean;
isFull: boolean;
currentDjId: string | null;
currentDjUsername: string | null;
playbackTrackId: string | null;
entries: Array<{
userId: string;
username: string;
displayUsername: string | null;
avatar: string | null;
role: string;
platformRole: string;
level: number | null;
xp: number | null;
fanCount: number | null;
infiniteLevel: boolean;
isFriend: boolean;
isFollowing: boolean;
position: number;
queuedTrackDurationMs: number | null;
estimatedWaitMs: number | null;
estimatedWaitKind: 'ready' | 'live' | 'unknown';
}>;
};
users: Array<{
id: string;
username: string;
displayUsername: string | null;
role: string;
platformRole: string;
avatar: string | null;
level: number | null;
xp: number | null;
fanCount: number | null;
infiniteLevel: boolean;
isFriend: boolean;
isFollowing: boolean;
}>;
social: {
friendIds: string[];
followingIds: string[];
friendsCount: number;
followingCount: number;
isFollowingCurrentDj: boolean;
isFriendWithCurrentDj: boolean;
};
progress: {
currentUser: {
id: string;
username: string;
level: number | null;
currentLevelXp: number | null;
xpRequired: number | null;
totalXp: number | null;
fanCount: number | null;
infiniteLevel: boolean;
} | null;
users: Array<{
userId: string;
username: string;
level: number | null;
xp: number | null;
fanCount: number | null;
infiniteLevel: boolean;
}>;
};
volume: number;
permissions: {
vote: boolean;
joinQueue: boolean;
sendChat: boolean;
};
};Subscribe to real-time updates:
const unsubscribe = window.WavezFM.room.subscribe(
'playback_changed',
(playback) => {
console.log('Track changed:', playback);
}
);
// Later
unsubscribe();| Event Name | Description |
|---|---|
room_changed |
Room metadata updated |
playback_changed |
New track / playback change |
votes_changed |
Vote counts updated |
queue_changed |
Queue state updated |
users_changed |
Users list updated |
chat_message |
New chat message received |
social_changed |
Social state updated |
progress_changed |
Progress data updated |
If you prefer native listeners:
window.addEventListener('WavezFM:playback_changed', (e) => {
console.log(e.detail);
});Available events:
WavezFM:room_changedWavezFM:playback_changedWavezFM:votes_changedWavezFM:queue_changedWavezFM:users_changedWavezFM:chat_messageWavezFM:social_changedWavezFM:progress_changed
All user-triggered interactions are exposed via:
window.WavezFM.actionsconst result = window.WavezFM.actions.vote('woot');'woot''meh'
type WavezActionResult = {
ok: boolean;
code:
| 'ok'
| 'unavailable'
| 'missing_room'
| 'missing_playback'
| 'self_vote_not_allowed';
requestId?: string | null;
};window.WavezFM.actions.joinQueue();Possible error codes:
current_djalready_in_queuequeue_lockedqueue_full
window.WavezFM.actions.leaveQueue();Possible error codes:
not_in_queue
window.WavezFM.actions.sendChat('hello room');Possible error codes:
invalid_contentrejected
window.WavezFM.actions.setVolume(25);- Range:
0 β 100 - Values are automatically clamped
Automatically votes once per track:
(() => {
const api = window.WavezFM;
if (!api || api.version !== '1') {
console.warn('WavezFM bridge unavailable');
return;
}
let lastPlaybackKey = null;
const voteForCurrentTrack = () => {
const state = api.room.getState();
const playback = state?.playback;
if (!playback) return;
if (playback.playbackKey === lastPlaybackKey) return;
lastPlaybackKey = playback.playbackKey;
if (!state.votes.canVote) return;
const result = api.actions.vote('woot');
console.log('AutoWoot result:', result);
};
voteForCurrentTrack();
api.room.subscribe('playback_changed', voteForCurrentTrack);
})();- Prefer the official bridge API over DOM queries or simulated UI interactions for core actions.
- Use
window.WavezFM.actionsfor voting, queue actions, chat, and volume control. - Use
playback.playbackKeyto detect track changes instead of comparing title or artist text. - Always handle
ok: falseaction results and branch on the returnedcode. - Use
requestIdonly for client-side logging and debugging; it is not a server-side identifier. votes_changedincludeswootUserIds,mehUserIds, andgrabUserIdsfor per-user reaction state.queue_changedincludes detailed queue entries, ETA data, and social/progression metadata.social_changedexposes friendship and following relationships resolved for the current room session.progress_changedexposes XP, level, and fan data for the current user and visible room users.- Treat
version: "1"as the compatibility key; future bridge versions may add more actions without changing the meaning of v1.