Skip to content

Commit 7b9ecec

Browse files
authored
Webgl3 (#19)
* shader * Initial webGL * Initial meme support. * Meme stuff. * WIP * Better rendering. * Scale better. * Add a setting for pixel ratio. * More improvements. * Add locator. * Render the username with HTML/CSS. * gud * Fix * ez. * ez * misc fixes * demo/audio prompt were missing. * Fix the fake. * Flip
1 parent 5b1a80b commit 7b9ecec

48 files changed

Lines changed: 2444 additions & 881 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/index.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,18 @@ main {
161161
input[type="range"] {
162162
accent-color: hsl(var(--link-hue), 75%, 50%);
163163
}
164+
165+
/* Locator throb animation */
166+
@keyframes throb {
167+
0%,
168+
100% {
169+
transform: scale(1);
170+
}
171+
50% {
172+
transform: scale(1.1);
173+
}
174+
}
175+
176+
.animate-throb {
177+
animation: throb 2s ease-in-out infinite;
178+
}

app/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
"scripts": {
88
"dev": "bunx --bun vite --open",
99
"build": "bunx --bun vite build",
10-
"check": "tsc --noEmit && biome check",
10+
"check": "tsc --noEmit && biome check && bun run check:shaders",
11+
"check:shaders": "glslangValidator src/room/gl/shaders/*.vert src/room/gl/shaders/*.frag",
1112
"fix": "biome check --fix",
1213
"tauri": "tauri"
1314
},

app/src/about.tsx

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
1+
import solid from "@kixelated/signals/solid";
12
import { createEffect, createSignal, type JSX, onCleanup } from "solid-js";
3+
import AudioPrompt from "./components/audio-prompt";
24
import CreateHang from "./components/create";
5+
import DemoHeader from "./components/demo-header";
36
import Layout from "./layout/web";
47
import { Canvas } from "./room/canvas";
58
import { FakeRoom } from "./room/fake";
69

710
export function About(): JSX.Element {
811
const canvas = <canvas class="border-3 border-link-hue rounded-lg w-full h-full" id="demo" />;
912

10-
const room = new FakeRoom(new Canvas(canvas as HTMLCanvasElement, { demo: true }));
13+
const room = new FakeRoom(new Canvas(canvas as HTMLCanvasElement));
1114
onCleanup(() => room.close());
1215

16+
const audioSuspended = solid(room.sound.suspended);
17+
18+
const handleEnableAudio = () => {
19+
room.sound.enabled.set(true);
20+
};
21+
1322
const services = ["Meet", "Zoom", "Teams", "Discord", "Skype", "WebEx", "FaceTime", "WhatsApp"];
1423
const [currentService, setCurrentService] = createSignal(0);
1524

@@ -109,11 +118,11 @@ export function About(): JSX.Element {
109118
() => {},
110119
() => {
111120
two.user.name.set("omni-chan");
112-
two.user.avatar.set("/avatar/omni.jpg");
121+
two.show(new URL("/avatar/omni.jpg", import.meta.url));
113122
},
114123
() => two.chat.typing.active.set(true),
115124
() => two.chat.message.latest.set("oops wrong button"),
116-
() => two.user.avatar.set("/avatar/43.svg"),
125+
() => two.stop(),
117126
() => three.chat.typing.active.set(true),
118127
() => three.chat.message.latest.set("dude"),
119128
() => two.play(new URL("/meme/linus.mp4", import.meta.url)),
@@ -251,17 +260,17 @@ export function About(): JSX.Element {
251260
</div>
252261
</div>
253262

254-
<div class="sm:m-8 m-4 h-128 w-full">{canvas}</div>
263+
<div class="sm:m-8 m-4 h-128 w-full relative">
264+
<DemoHeader />
265+
<AudioPrompt show={audioSuspended()} onClick={handleEnableAudio} />
266+
{canvas}
267+
</div>
255268

256269
<p>
257270
Powered by new and <a href="https://github.com/kixelated/moq">open source</a> web tech:{" "}
258271
<a href="https://moq.dev">MoQ</a>. There's more to live than another {services[currentService()]}{" "}
259272
clone. <i>Crazy</i>, I know.
260273
</p>
261-
262-
<div class="flex my-18">
263-
<img src="/image/we-are/5.svg" alt="we are live" class="max-w-120 w-full" />
264-
</div>
265274
</div>
266275
</Layout>
267276
);

app/src/account.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ function AccountLoad(): JSX.Element {
114114
<div class="bg-gray-900 border border-gray-700 rounded-2xl p-8 max-w-md mx-4">
115115
<h2 class="text-2xl font-bold mb-4 text-red-400">Delete Account?</h2>
116116
<p class="text-gray-300 mb-6">
117-
This action cannot be undone. Your account, profile information, and all associated data will be
118-
permanently deleted.
117+
This action cannot be undone. Your account, profile information, and all associated data
118+
will be permanently deleted.
119119
</p>
120120
<div class="flex gap-4">
121121
<button
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Show } from "solid-js";
2+
import type { JSX } from "solid-js/jsx-runtime";
3+
4+
export default function AudioPrompt(props: { show: boolean; onClick: () => void }): JSX.Element {
5+
return (
6+
<Show when={props.show}>
7+
<div class="absolute bottom-0 left-0 right-0 flex items-center justify-center z-10 m-4">
8+
<button
9+
type="button"
10+
onClick={props.onClick}
11+
class="backdrop-blur-sm rounded-2xl px-8 py-4 text-white transition-all shadow-2xl hover:scale-105 cursor-pointer"
12+
>
13+
<div class="flex items-center gap-3">
14+
<span class="icon-[mdi--volume-mute] w-6 h-6 text-red-500" />
15+
<span class="text-lg font-semibold">Click to enable audio</span>
16+
</div>
17+
</button>
18+
</div>
19+
</Show>
20+
);
21+
}

app/src/components/badge.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,20 @@ import * as Tauri from "../tauri";
44

55
async function set(count: number | undefined) {
66
if (Tauri.Api) {
7-
await Tauri.Api.window
7+
const success = await Tauri.Api.window
88
.getCurrentWindow()
99
.setBadgeCount(count || undefined)
10-
.catch((error) => console.warn("Failed to set Tauri badge:", error));
11-
} else if (navigator.setAppBadge) {
12-
await navigator
10+
.then(() => true)
11+
.catch(() => false);
12+
if (success) return;
13+
}
14+
15+
if (navigator.setAppBadge) {
16+
const success = await navigator
1317
.setAppBadge(count || undefined)
14-
.catch((error) => console.warn("Failed to set Web badge:", error));
18+
.then(() => true)
19+
.catch(() => false);
20+
if (success) return;
1521
}
1622
}
1723

app/src/components/demo-header.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import type { JSX } from "solid-js/jsx-runtime";
2+
3+
export default function DemoHeader(): JSX.Element {
4+
return (
5+
<div class="absolute top-0 left-0 m-4 z-10 px-4 py-2 bg-black/70 backdrop-blur-sm rounded-lg">
6+
<div class="text-2xl font-bold text-white/90 underline decoration-link-hue underline-offset-2">DEMO</div>
7+
</div>
8+
);
9+
}

app/src/components/profile.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ class LocalPreview {
124124

125125
constructor(element: HTMLCanvasElement, camera: Publish.Broadcast) {
126126
// Create a minimal canvas without the background effects
127-
this.canvas = new Canvas(element, { demo: false });
127+
this.canvas = new Canvas(element);
128128

129129
// Create a minimal sound context (muted for preview)
130130
this.sound = new Sound();

app/src/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { Canvas } from "./room/canvas";
2525
import { Sup } from "./sup";
2626

2727
export function Hang(): JSX.Element {
28-
const background = (<canvas class="fixed inset-0 w-full h-full" />) as HTMLCanvasElement;
28+
const background = (<canvas class="fixed inset-0 w-full h-full bg-black" />) as HTMLCanvasElement;
2929
const canvas = new Canvas(background);
3030
onCleanup(() => canvas.close());
3131

app/src/privacy.tsx

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,32 +8,41 @@ export default function Privacy() {
88
<p class="text-sm text-gray-600 mb-8">Last Updated: October 2, 2025</p>
99

1010
<section class="mb-8">
11-
<h2 class="text-2xl font-semibold mb-4 underline decoration-link-hue underline-offset-2">Philosophy</h2>
11+
<h2 class="text-2xl font-semibold mb-4 underline decoration-link-hue underline-offset-2">
12+
Philosophy
13+
</h2>
1214
<p>
13-
We believe in the right to privacy.
14-
Have fun and be weird; you're not being judged (by us at least).
15+
We believe in the right to privacy. Have fun and be weird; you're not being judged (by us at
16+
least).
1517
</p>
1618
</section>
1719

1820
<section class="mb-8">
19-
<h2 class="text-2xl font-semibold mb-4 underline decoration-link-hue underline-offset-2">What We Collect</h2>
21+
<h2 class="text-2xl font-semibold mb-4 underline decoration-link-hue underline-offset-2">
22+
What We Collect
23+
</h2>
2024
<p class="mb-4">We collect minimal information to provide our service:</p>
2125
<ul class="list-disc pl-6 space-y-2">
2226
<li>
23-
<strong>Account Information</strong>: When you sign in with a linked provider (ex. Google, Discord, or Apple), we store
24-
your email address, display name, and avatar. You can replace your name and avatar at any time.
27+
<strong>Account Information</strong>: When you sign in with a linked provider (ex. Google,
28+
Discord, or Apple), we store your email address, display name, and avatar. You can replace
29+
your name and avatar at any time.
2530
</li>
2631
<li>
27-
<strong>Session State</strong>: An authentication token and any user preferences are stored in your browser's local storage. It is cleared when you log out.
32+
<strong>Session State</strong>: An authentication token and any user preferences are stored
33+
in your browser's local storage. It is cleared when you log out.
2834
</li>
2935
<li>
30-
<strong>Media Cache</strong>: We cache seconds worth of media to improve the playback experience. It is cleared immediately after disconnecting.
36+
<strong>Media Cache</strong>: We cache seconds worth of media to improve the playback
37+
experience. It is cleared immediately after disconnecting.
3138
</li>
3239
</ul>
3340
</section>
3441

3542
<section class="mb-8">
36-
<h2 class="text-2xl font-semibold mb-4 underline decoration-link-hue underline-offset-2">What We Don't Collect</h2>
43+
<h2 class="text-2xl font-semibold mb-4 underline decoration-link-hue underline-offset-2">
44+
What We Don't Collect
45+
</h2>
3746
<p class="mb-4">We do not:</p>
3847
<ul class="list-disc pl-6 space-y-2">
3948
<li>Store any video/audio/conversations</li>
@@ -44,7 +53,9 @@ export default function Privacy() {
4453
</section>
4554

4655
<section class="mb-8">
47-
<h2 class="text-2xl font-semibold mb-4 underline decoration-link-hue underline-offset-2">How We Use Your Information</h2>
56+
<h2 class="text-2xl font-semibold mb-4 underline decoration-link-hue underline-offset-2">
57+
How We Use Your Information
58+
</h2>
4859
<p class="mb-4">Your account information is used to:</p>
4960
<ul class="list-disc pl-6 space-y-2">
5061
<li>Identify you when you're logged in</li>
@@ -53,7 +64,9 @@ export default function Privacy() {
5364
</section>
5465

5566
<section class="mb-8">
56-
<h2 class="text-2xl font-semibold mb-4 underline decoration-link-hue underline-offset-2">Data Storage</h2>
67+
<h2 class="text-2xl font-semibold mb-4 underline decoration-link-hue underline-offset-2">
68+
Data Storage
69+
</h2>
5770
<ul class="list-disc pl-6 space-y-2">
5871
<li>Account information is stored securely on our servers</li>
5972
<li>Authentication tokens are stored locally in your browser</li>
@@ -62,15 +75,19 @@ export default function Privacy() {
6275
</section>
6376

6477
<section class="mb-8">
65-
<h2 class="text-2xl font-semibold mb-4 underline decoration-link-hue underline-offset-2">Third-Party Authentication</h2>
78+
<h2 class="text-2xl font-semibold mb-4 underline decoration-link-hue underline-offset-2">
79+
Third-Party Authentication
80+
</h2>
6681
<p>
6782
We use Google, Discord, and Apple for sign-in. When you authenticate, you're subject to their
6883
privacy policies. We only receive basic profile information with your consent.
6984
</p>
7085
</section>
7186

7287
<section class="mb-8">
73-
<h2 class="text-2xl font-semibold mb-4 underline decoration-link-hue underline-offset-2">Data Sharing</h2>
88+
<h2 class="text-2xl font-semibold mb-4 underline decoration-link-hue underline-offset-2">
89+
Data Sharing
90+
</h2>
7491
<ul class="list-disc pl-6 space-y-2">
7592
<li>We do not sell, rent, or share your information</li>
7693
<li>We do not display ads or work with advertisers</li>
@@ -79,7 +96,9 @@ export default function Privacy() {
7996
</section>
8097

8198
<section class="mb-8">
82-
<h2 class="text-2xl font-semibold mb-4 underline decoration-link-hue underline-offset-2">Hang Privacy</h2>
99+
<h2 class="text-2xl font-semibold mb-4 underline decoration-link-hue underline-offset-2">
100+
Hang Privacy
101+
</h2>
83102
<ul class="list-disc pl-6 space-y-2">
84103
<li>All hangs are public to anyone with the URL</li>
85104
<li>Do not share sensitive information in hangs</li>
@@ -89,24 +108,40 @@ export default function Privacy() {
89108
</section>
90109

91110
<section class="mb-8">
92-
<h2 class="text-2xl font-semibold mb-4 underline decoration-link-hue underline-offset-2">Your Rights</h2>
111+
<h2 class="text-2xl font-semibold mb-4 underline decoration-link-hue underline-offset-2">
112+
Your Rights
113+
</h2>
93114
<ul class="list-disc pl-6 space-y-2">
94-
<li>Delete your account from the <a href="/account" class="text-blue-400 hover:underline">account settings page</a></li>
115+
<li>
116+
Delete your account from the{" "}
117+
<a href="/account" class="text-blue-400 hover:underline">
118+
account settings page
119+
</a>
120+
</li>
95121
<li>Logout or clear browser storage to remove any local state</li>
96-
<li>Request a copy of your stored information by contacting <a href="mailto:admin@hang.live">admin@hang.live</a></li>
122+
<li>
123+
Request a copy of your stored information by contacting{" "}
124+
<a href="mailto:admin@hang.live">admin@hang.live</a>
125+
</li>
97126
</ul>
98127
</section>
99128

100129
<section class="mb-8">
101-
<h2 class="text-2xl font-semibold mb-4 underline decoration-link-hue underline-offset-2">Changes to This Policy</h2>
130+
<h2 class="text-2xl font-semibold mb-4 underline decoration-link-hue underline-offset-2">
131+
Changes to This Policy
132+
</h2>
102133
<p>We'll notify users via email of significant policy changes.</p>
103134
</section>
104135

105136
<section class="mb-8">
106-
<h2 class="text-2xl font-semibold mb-4 underline decoration-link-hue underline-offset-2">Contact</h2>
107-
<p>For questions: <a href="mailto:admin@hang.live">admin@hang.live</a></p>
137+
<h2 class="text-2xl font-semibold mb-4 underline decoration-link-hue underline-offset-2">
138+
Contact
139+
</h2>
140+
<p>
141+
For questions: <a href="mailto:admin@hang.live">admin@hang.live</a>
142+
</p>
108143
</section>
109144
</div>
110145
</Layout>
111-
)
146+
);
112147
}

0 commit comments

Comments
 (0)