PageSourceSearch

https://pooled.run/room/preflight.js

js pooled.run collected 2026-10-09 22:55:31 UTC 3,794 bytes, 57 lines download raw bytes

1// Pre-flight: can this browser hold layers, and if not, what exactly to do about it. The verdict
2// is a pure function of what the browser reports, so it can be unit tested; probe() gathers it.
3
4// iPadOS reports a Mac user agent; a touch screen gives it away
5export function deviceKind({ ua = "", touchPoints = 0, mobile = false } = {}) {
6  if (/iPhone|iPod/.test(ua)) return "iPhone";
7  if (/iPad/.test(ua) || (/Macintosh/.test(ua) && touchPoints > 1)) return "iPad";
8  if (/Android/.test(ua)) return mobile || /Mobile/.test(ua) ? "Android" : "Android tablet";
9  if (/Mac/.test(ua)) return "Mac";
10  return "Device";
11}
12
13// facts: { ua, secure, hasGpuApi, adapter: null | { vendor, architecture }, touchPoints, mobile }
14// -> { ok, kind, line, detail }: one calm sentence (what this device can do), and the remedy
15// (what to change for WebGPU) for a Details disclosure. Phones are checked first, in phone words.
16const PHONE = "This phone's GPU isn't available to the browser. It can still join and chat.";
17const DESK = "This browser can't use its graphics chip, so this device can chat but not help run the model. Chrome or Edge on a laptop works best.";
18export function verdict(f) {
19  const kind = deviceKind(f);
20  const ua = f.ua || "";
21  if (f.adapter) {
22    const g = [f.adapter.vendor, f.adapter.architecture].filter(
22Boolean).join(" ");
23    return { ok: true, kind, line: `WebGPU works here${g ? ` (${g})` : ""}: this ${kind === "Device" ? "device" : kind} can hold part of the model.`, detail: "" };
24  }
25  const no = (line, detail) => ({ ok: false, kind, line, detail });
26  const phone = kind === "iPhone" || kind === "iPad" || kind === "Android" || kind === "Android tablet";
27  if (phone) {
28    if (!f.secure) return no(PHONE, "WebGPU needs a secure page: open this over https.");
29    if (kind === "iPhone" || kind === "iPad") return no(PHONE, `WebGPU on an ${kind} needs Safari 26 (iOS 26) or later: update iOS.`);
30    if (f.hasGpuApi) return no(PHONE, "The browser has WebGPU but offered no GPU on this phone (a blocklisted driver?). Chrome 121 or later may help.");
31    return no(PHONE, "This Android browser has no WebGPU: use Chrome 121 or later.");
32  }
33  // when the fix is in this browser (a flag, an update), the line says so instead of "use Chrome",
34  // and the page shows the remedy open, not behind a collapsed Details
35  const CHAT = "so this device can chat but not help run the model yet.";
36  if (!f.secure) return no(`This page isn't secure, ${CHAT}`, "WebGPU needs a secure page: open this over https (or localhost).");
37  if (f.hasGpuApi) return no(`This browser has WebGPU but found no usable GPU, ${CHAT}`, "A blocklisted driver? Try chrome://flags/#enable-unsafe-webgpu, then reload, or another browser.");
38  if (/Firefox\//.test(ua)) return no(`Firefox has no WebGPU here, ${CHAT}`, "Use Chrome or Edge 113+, or Safari 26+.");
39  if (/Linux/.test(ua) && /Chrome\//.test(ua) && !/Android/.test(ua))
40    return no(`WebGPU is off in Chrome on Linux, ${CHAT} Two flags turn it on:`, "Enable chrome://flags/#enable-unsafe-webgpu and chrome://flags/#enable-vulkan, then reload.");
41  if (/Safari\//.test(ua) && !/Chrome\//.test(ua)) return no(`This Safari has no WebGPU, ${CHAT}`, "Safari 26 has it: update macOS, or use Chrome.");
42  return no(DESK, "Use Chrome or Edge 113+, or Safari 26+.");
43}
44
45export async function probe() {
46  const f = {
47    ua: navigator.userAgent, secure: window.isSecureContext, hasGpuApi: !!navigator.gpu,
48    touchPoints: navigator.maxTouchPoints || 0, mobile: !!navigator.userAgentData?.mobile, adapter: null,
49  };
50  if (navigator.gpu) {
51    try {
52      const a = await navigator.gpu.requestAdapter();
53      if (a) f.adapter = { vendor: a.info?.vendor || "", architecture: a.info?.architecture || "" };
54    } catch {}
55  }
56  return verdict(f);
57}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.