A realtime, Supabase-backed web chat built with plain HTML/CSS/JS. Includes:
- 1-to-1 DMs (stored in
public.messages) - A public
#GLOBALchannel (stored inpublic.channel_messages)
- Supabase Auth login (username/password)
- Realtime DM updates (new messages appear instantly)
- Realtime public channel (
#GLOBAL) - Presence dots (online/offline)
- Typing indicator (broadcast)
- Slash commands:
/help,/who,/clear,/lock [n],/top,/color list,/color set <number>,/theme list,/theme set <name>,/mute - UI themes and curated identity colors
- Frontend:
index.html,java.js,style.css(static site) - Backend: Supabase (Postgres + RLS + Realtime + Presence)
Create a project at supabase.com.
In Supabase dashboard: disable email confirmation (the app treats the username as the email local-part).
This repo includes sql.md which contains:
public.profilespublic.messages- RLS policies and realtime publication settings
- migrations for locking (
lockedcolumn) and supporting realtime lock sync
Important: the app also uses public.channel_messages for #GLOBAL. If your Supabase project does not already have that table, create it first (see next section), then run sql.md.
Run this in the Supabase SQL editor:
create table if not exists public.channel_messages (
id bigserial primary key,
sender_id uuid not null references auth.users(id) on delete cascade,
channel text not null,
body text not null,
locked boolean not null default false,
created_at timestamptz not null default now(),
constraint body_length check (char_length(trim(body)) between 1 and 10000)
);
alter table public.channel_messages enable row level security;
-- Select: allow authenticated users to read public channels
create policy "channel_messages: authenticated users can read"
on public.channel_messages for select to authenticated using (true);
-- Insert: users can only insert as themselves
create policy "channel_messages: sender can insert"
on public.channel_messages for insert to authenticated
with check (auth.uid() = sender_id);
-- Update: allow toggling the `locked` field (message lock sync)
create policy "channel_messages: allow locking updates"
on public.channel_messages for update to authenticated
using (true);
-- Realtime needs full replica identity so `payload.old` works for lock updates
alter table public.channel_messages replica identity full;
-- Add the table to Supabase Realtime publication
alter publication supabase_realtime add table channel_messages;sql.md adds messages and profiles to supabase_realtime. Ensure channel_messages is also included (the snippet above does this).
java.js contains:
SUPABASE_URLSUPABASE_ANON
Replace those values with your own Supabase project settings.
Serve the folder over HTTP (do not open via file://).
From this folder:
py -m http.server 5173Then open: http://localhost:5173
- Open the app in your browser.
- Sign up / log in.
- Choose a contact to chat via DM, or select
# GLOBALat the top for the public channel. - Use
/helpto see commands.
- The app uses username-as-email by appending
@theeye.localfor Supabase Auth. - Audio notifications require a user interaction (browser autoplay policies). The app unlocks audio after the first play attempt.