Last week Kent C. Dodds launched his magnum opus, a fullstack web development course called Epic Web. The course is massive in scale - it consists of 452 videos divided into 56 sections, with additional snacks like 25 interviews with experts. It was an instant success and has been celebrated enthusiastically by the web dev community.
We couldnāt be prouder to be a part of this amazing feat. Right before the launch we worked with Kent and added presence to the course platform (see first PR and second PR). Presence is a way to create a sense of shared online experience among your users for example by showing their cursors (like in Figma), the place in text they are (like in Google Docs), or by featuring your usersā avatars, which is what Kent went with.
Presence in Epic Web
On the left side of the platform screen, users can see their own profile and a number of other users working on the same lesson ā even though the app runs on localhost
:
When you open the sidebar, you can see all the avatars as well:
Since the launch, the platform has been sending over 100,000 events a day to PartyKit - a testament to how popular the course is.
Adding avatars to your website
All PartyKit server code that was needed to implement the real-time avatars feature to Kentās course was the following:
import type * as Party from "partykit/server";
type UserPayload = {
id: string;
avatarUrl: string;
name?: string | null | undefined;
};
type Message =
| { type: "remove-user"; payload: Pick<UserPayload, "id"> }
| { type: "add-user"; payload: UserPayload }
| { type: "presence"; payload: { users: Array<UserPayload> } };
export default class Server implements Party.Server {
options: Party.ServerOptions = { hibernate: true };
constructor(party: Party.Party) {
this.party = party;
}
updateUsers() {
const presenceMessage = JSON.stringify(this.getPresenceMessage());
for (const connection of this.party.getConnections<UserPayload>()) {
connection.send(presenceMessage);
}
}
getPresenceMessage(): Message {
const users = new Map<string, UserPayload>();
for (const connection of this.party.getConnections<UserPayload>()) {
const user = connection.state;
if (user) users.set(user.id, user);
}
return {
type: "presence",
payload: { users: Array.from(users.values()) },
} satisfies Message;
}
onMessage(message: string, sender: Party.Connection<UserPayload>) {
const user = JSON.parse(message) as Message;
if (user.type === "add-user") {
sender.setState(user.payload);
this.updateUsers();
} else if (user.type === "remove-user") {
sender.setState(null);
this.updateUsers();
}
}
onClose() {
this.updateUsers();
}
onError() {
this.updateUsers();
}
}
Kent later improved the UX to also group the users according to their progress.
Moreover, as privacy is an important consideration, he implemented an option to opt out of avatars, taking full advantage of that PartyKit allows you to write your own business logic.
You can see the full code here:
Reactions to presence
Learning online often feels like a lonely journey so realtime features added to online courses can help students enjoy the ride a little more.
Responses to Kentās tweets were enthusiatic and positive:
Presence features like avatars, cursors, chat, or visible text highlighting are generally a well-received idea. Websites, apps, courses, and online experiences feel less lonely and isolated if you know that there are others sharing this moment with you.
Letās make the web friendlier!
The web was created to connect people and yet, it often feels like a lonely place. Weāre here to help because everythingās better with friends š„°
Iād love to help you make your website or app feel more familiar. Reach out to me on PartyKit Discord or reach out to me on Twitter!
Alternatively, we also have some code examples for you to get inspired:
Letās make the web friendlier!