Two ways to gather: keep it simple and private, or build a public, Meetup-style group people can discover, join, and keep coming back to.
localStorage so it's
only visible in this one browser. To make it real:
GET /api/groups?search=&tradition=,
GET /api/groups/:id/events — and replace loadAllGroups() / loadAllEvents()
below with fetches.
createGroup()'s and createHostedEvent()'s local saves with
POST /api/groups and POST /api/groups/:id/events.
AI_TEACHERS / HUMAN_TEACHERS lists below for a live pull
from the real teacher directory (the data behind portals.html) — already generated from portals.html's real
Portal 1 / Portal 3 rosters; regenerate the same way if that roster changes.
switchMode('private'))
is a simple one-off event with no group, no listing, and no fee — unchanged in spirit from before. Meetup-Style
is now a real Groups + Events model, like Meetup itself: an organizer creates a persistent,
public Group (name, tradition, description), then schedules any number of Events
under it. Organizer Access gates creating a Group, not each event — see
gathering-donations-dev-spec.md v5 for the $6/mo ($3/mo for MST plan members after free
trial months) pricing; once you own a group, scheduling more events under it is free, the same as Meetup
charges for the group/listing tool, not per meetup. hasActiveOrganizerAccess() /
getOrganizerAccess() / activateOrganizerAccess() are unchanged prototype
stand-ins for a real subscription check — still client-side only, still needs a real Stripe checkout and a
server-side check on POST /api/groups before launch.
group.organizer_id == current_user.id server-side before allowing
POST /api/groups/:id/events.
hostMedium). Meetup-Style events use checkboxes instead (mediums array),
so an organizer can mark an event as Zoom, In-Person, or both at once (hybrid) — closer to how real
Meetup groups actually run events. All rendering reads ev.mediums (array); older single-medium
records are read as a one-item array for backward compatibility.
mst_groups_index + mst_group_<id> for groups;
mst_group_events_index + mst_group_event_<id> for events (now carrying a
groupId, null for private ones). Search/filter (renderGroupsGrid()) is a client-side
filter over localStorage — swap for real query params once there's a backend.
.meetup-theme), distinct from the site's dark shell — both to make
the mode toggle feel meaningful, and to preview what this could look like if it's spun off into its own
standalone site later. Private Scheduling keeps the site's normal dark aesthetic, unchanged.
ensureSampleData()) exist purely so this isn't an empty page on
first load — tagged isSample:true, shown with a "Preview" chip. Delete once real groups exist.
avatar-renderer.js only renders the avatar inside our own session.html page;
nothing pushes it into an actual Zoom call. You'd need either (a) Zoom's Video SDK to add the avatar as a bot
participant, or (b) a virtual-camera pipeline so people can select it as their camera source inside Zoom.
Until then, the realistic near-term version is what's built here: the avatar lives in our own Live Session
Room, kept open on a shared screen or device.
#avatar-join-note on the
event page) currently only appears once someone is actually setting up or viewing a specific gathering —
the moment it's actually relevant, not before. We discussed also surfacing a shorter teaser version earlier
in the funnel (e.g. on community.html, or the homepage's "Host Your Own Gathering" blurb) for
people who are still deciding whether to host/attend and might have "wait, how does the avatar actually
show up?" as an unspoken hesitation. Decided against it for now since it's a conversion nice-to-have, not
a usability gap — revisit if that hesitation turns out to be common once there's real usage data. Would
need community.html uploaded/reviewed to implement.
ALL_TRADITIONS,
populateTraditionSelect(), handleTraditionSelectChange()): every tradition
picker — the private and Meetup-Style filters, group creation, and the self-led gathering tradition
field — shows the full canonical list (from AI_TEACHERS) rather than only traditions that
already have content, plus a trailing "+ Create a Tradition" option on the creation flows (not the
filters, where "creating" one doesn't make sense). Selecting it uses window.prompt() to add
a new option live and unmoderated — fine for a prototype, but a real launch should route custom traditions
through some kind of lightweight review step rather than adding them straight to a shared list, to avoid
spam/duplicate/inappropriate entries. Also fixed a real gap this surfaced: self-led gatherings previously
saved path: '' unconditionally, so they could never be found via tradition filtering — they
now save whatever's chosen in the new #h-self-tradition field (defaults to "Open / Various").
DIGITAL_TWIN_TEACHERS,
pulled from portals.html's Portal 2 roster — Eckhart Tolle, Thomas Merton, Jim Newman, Ramana Maharshi,
John Astin). "Who's Guiding It" (host form) and "Centered Around" (create-group form) now offer AI Avatar
/ Digital Twin / Human Teacher / (Self, host form only) — checked against all three portals on
portals.html to confirm no traditions were missing from ALL_TRADITIONS either (Portal 2's
traditions are all already covered by Portal 1's list). teacherType: 'twin' is treated the
same as 'ai' everywhere that means "this teacher renders as an avatar via session.html" (the
Live Session Room note, Zoom join copy, the demo session link) — the two are only visually distinguished
by their tag ("✦ AI Avatar" vs "◈ Digital Twin"), since a digital twin is still avatar-rendered, just
modeled on a specific real person rather than a generic guide. Also extended the Meetup-Style search bar
to match on group.teacherName, not just the group's own name/description/tradition — since
people search for a specific teacher by name (e.g. "Ramana Maharshi") as often as they search by
tradition.
createHostedEvent(), when hostContext is a group id, not
'private'), email every existing member of that group. "Member of the group" isn't
currently a distinct concept from "attendee of one of its events" — see point (d) below, this needs
deciding before the send list can be built.
event.attendees), roughly 7 days before event.date/event.time.
rsvpToEvent() / cancelRsvp()) right up until the event.
ev.attendees) — there's no
group.members list anywhere. If you want (a) above to work like real Meetup, a "Join Group"
action and a persisted members list need to be added first; simplest version is a members table keyed by
groupId + email, separate from any one event's attendee list.
ev.mediums), the suggested donation if any, and a link straight to the
event page.
localStorage, it needs a server-side source of truth.
POST /api/groups/:id/events succeeds, no scheduler needed for that one.
event.date/event.time are currently plain
strings with no time zone attached anywhere in the prototype — needs fixing before "1 week before" /
"1 day before" can be computed correctly for people outside the organizer's zone.
Schedule a single gathering and share the link directly — a small circle or a large congregation, either way. It's never listed publicly and always free, no Organizer Access required.
Browse public groups across every tradition, join one, or start your own and schedule events under it — search by name, or narrow it down by tradition.
Joining any group or event is always free. A small Organizer Access fee applies only to starting your own public group — scheduling events under a group you already own is included.
Starting a public group needs active Organizer Access — a small flat fee, nothing to do with any plan.
Not nowGive it a name and a tradition — you'll schedule your first event right after.
Set it up, share the link, and watch the roster fill in.
See who else is coming, then reserve your spot — visible to your teacher and every other participant, like a Meetup event page.
localStorage — visible only within this one browser,
not shared between real people on different devices. To make it real:
loadEvent() / saveEvent() below with
GET /api/events/:id and POST /api/events/:id/rsvp.
The organizer has shared a Zoom link for this gathering.
Join via Zoom →The organizer hasn't added a Zoom link yet — it'll show up here once they do.
Whether you're meeting one-on-one, as a small circle, or as a full group — on Zoom or in person — the teacher shows up the same way right now: open the Live Session Room on one device and put it on a shared screen. On Zoom, that's a second window shared to the call, or a second device pointed at the group; in person, it's a laptop, tablet, or TV everyone gathers around.
Once it's up, the avatar interacts and participates fully and in real time — listening, responding, leading meditation, answering questions — exactly as it would if it were a native participant in the call. The only difference is where it's displayed: on its own shared screen rather than as its own video tile in the Zoom grid.
Your teacher and everyone else joining can see you're coming. We'll send a reminder as the session gets closer, along with your join link.
Cancel my spotBoth paths lead to the same place — choose whichever feels right.