diff --git a/docs/dannom-feature-requests.md b/docs/dannom-feature-requests.md
index f759bd06b4..54bfb5c510 100644
--- a/docs/dannom-feature-requests.md
+++ b/docs/dannom-feature-requests.md
@@ -17,7 +17,7 @@ Captured from 6,478 public `laer-klokken` messages between June 1 and August 8,
| DAN-009 | Needs validation | Teach sentences or grammar as well as vocabulary. | `2026-07-14 19:39` — `@q13R608tx`: “dannom is only good for general vocabulary”; `@theamerican`: “you won't learn full sentenses.” | Product feedback rather than a direct feature request. Explore as a distinct mode so the word-munching game stays legible. |
| DAN-010 | Closed; no reproduction | Track transient reports that Dannom disappeared. | `2026-08-03 20:46` — “rip dannom”; `21:37` — “is dannom gone?”; `21:38` — “its still here.” | Self-resolved in chat. Reopen only with a reproducible load failure or piece-log evidence. |
| DAN-011 | Implemented locally | Brand Nom HUDs with Comic Relief and show cute handled identities. | Product direction, August 7. | Browser HD text loads the foundry Comic Relief face. The score HUD and leaderboard paint handles character-by-character with their saved colors, with a generated color fallback. Word tiles retain Unicode-safe text. |
-| DAN-012 | Implemented locally; device verification pending | Run the shared Nom engine in the OSKIEWAR-generation Xbox stack. | Product direction, August 7. | `xbox/tools/bundle-nom.mjs` produces a QuickJS `xbox/live/dannom.js` from the real Nom, scoring, synth, and percussion sources. The adapter uses fixed-step pad edges, native drawing/audio, and packaged `comicWrite`; its command-frame probe and rendered 1280×720 frame pass locally. Deploy against the current OSKIEWAR native BIOS remains to be verified on Xbox hardware. |
+| DAN-012 | Implemented locally; device verification pending | Run the shared Nom engine in the oskiewar-generation Xbox stack. | Product direction, August 7. | `xbox/tools/bundle-nom.mjs` produces a QuickJS `xbox/live/dannom.js` from the real Nom, scoring, synth, and percussion sources. The adapter uses fixed-step pad edges, native drawing/audio, and packaged `comicWrite`; its command-frame probe and rendered 1280×720 frame pass locally. Deploy against the current oskiewar native BIOS remains to be verified on Xbox hardware. |
## Not promoted
diff --git a/lith/deploy.fish b/lith/deploy.fish
index a49732dab6..98e618f294 100644
--- a/lith/deploy.fish
+++ b/lith/deploy.fish
@@ -207,9 +207,9 @@ git rev-parse HEAD > system/public/.commit-ref"
exit 1
end
-echo -e "$GREEN-> Verifying OSKIEWAR social preview freshness...$NC"
+echo -e "$GREEN-> Verifying oskiewar social preview freshness...$NC"
if not ssh -i $SSH_KEY $LITH_USER@$TARGET_HOST "cd $REMOTE_DIR && node xbox/live/render-social-preview.mjs --check"
- echo -e "$RED x OSKIEWAR social preview is stale; restoring $PREVIOUS_HEAD.$NC"
+ echo -e "$RED x oskiewar social preview is stale; restoring $PREVIOUS_HEAD.$NC"
ssh -i $SSH_KEY $LITH_USER@$TARGET_HOST "\
cd $REMOTE_DIR && \
git reset --hard $PREVIOUS_HEAD --quiet && \
diff --git a/lith/webhook.sh b/lith/webhook.sh
index 00c3a9d91e..4c479c4b62 100755
--- a/lith/webhook.sh
+++ b/lith/webhook.sh
@@ -60,7 +60,7 @@ if [ "$OLD_HEAD" = "$NEW_HEAD" ]; then
fi
if ! node xbox/live/render-social-preview.mjs --check; then
- log "OSKIEWAR social preview is stale; restoring $OLD_HEAD"
+ log "oskiewar social preview is stale; restoring $OLD_HEAD"
git reset --hard "$OLD_HEAD" --quiet
exit 1
fi
diff --git a/package.json b/package.json
index 8735ab556d..5471103364 100644
--- a/package.json
+++ b/package.json
@@ -32,6 +32,8 @@
"xbox:test:oskiewar:blackbox": "node xbox/live/tests/blackbox-rounds.mjs",
"xbox:burn:oskiewar-social": "node xbox/live/render-social-preview.mjs",
"xbox:check:oskiewar-social": "node xbox/live/render-social-preview.mjs --check",
+ "oskiewar:mac:build": "sh xbox/tools/build-macos-app.sh",
+ "oskiewar:mac:install": "sh xbox/tools/build-macos-app.sh --install",
"test:nopaint:e2e": "node tests/browser/nopaint-journey.test.mjs",
"test:nopaint:e2e:local": "node tests/browser/run-nopaint-local.mjs",
"test:nopaint:e2e:film": "AC_HEADED=1 AC_SLOWMO=120 AC_FRAME_RECEIPTS=1 node tests/browser/nopaint-journey.test.mjs",
diff --git a/papers/arxiv-oskiewar/README.md b/papers/arxiv-oskiewar/README.md
index 2c6cc76636..ae0214d719 100644
--- a/papers/arxiv-oskiewar/README.md
+++ b/papers/arxiv-oskiewar/README.md
@@ -1,4 +1,4 @@
-# OSKIEWAR
+# oskiewar
Archival system report for the native Xbox game, web spectator, and shared-client direction.
diff --git a/papers/arxiv-oskiewar/oskiewar.tex b/papers/arxiv-oskiewar/oskiewar.tex
index 209e183e9a..e54a9830fe 100644
--- a/papers/arxiv-oskiewar/oskiewar.tex
+++ b/papers/arxiv-oskiewar/oskiewar.tex
@@ -39,7 +39,7 @@
\hypersetup{colorlinks=true,linkcolor=acblue,urlcolor=acblue,citecolor=acblue,
pdfauthor={@jeffrey},
- pdftitle={OSKIEWAR: A Cross-Platform Fighting-Game Laboratory for Aesthetic Computer}}
+ pdftitle={oskiewar: A Cross-Platform Fighting-Game Laboratory for Aesthetic Computer}}
\titleformat{\section}{\normalfont\bfseries\normalsize\uppercase}{\thesection.}{0.45em}{}
\titlespacing{\section}{0pt}{1.15em}{0.3em}
\titleformat{\subsection}{\normalfont\bfseries\small}{\thesubsection}{0.45em}{}
@@ -47,7 +47,7 @@
\pagestyle{fancy}
\fancyhf{}
\renewcommand{\headrulewidth}{0pt}
-\fancyhead[C]{\footnotesize\color{acpink}\textit{OSKIEWAR --- system report}}
+\fancyhead[C]{\footnotesize\color{acpink}\textit{oskiewar --- system report}}
\fancyfoot[C]{\footnotesize\thepage}
\setlist[itemize]{nosep,leftmargin=1.2em,itemsep=0.1em}
\setlength{\columnsep}{1.7em}
@@ -61,7 +61,7 @@
\emergencystretch=1em
\graphicspath{{../arxiv-ac/figures/}}
\newcommand{\ac}{\textsc{Aesthetic.Computer}}
-\newcommand{\game}{\textsc{OSKIEWAR}}
+\newcommand{\game}{\textsc{oskiewar}}
\newcommand{\code}[1]{\texttt{#1}}
\InputIfFileExists{version.tex}{}{%
\providecommand{\paperhash}{dev}\providecommand{\paperrev}{?}}
@@ -75,7 +75,7 @@
\twocolumn[{%
\begin{center}
\includegraphics[height=3.5em]{pals}\par\vspace{0.45em}
-{\acbold\fontsize{23pt}{27pt}\selectfont\color{acdark} OSKIEWAR}\par
+{\acbold\fontsize{23pt}{27pt}\selectfont\color{acdark} oskiewar}\par
\vspace{0.2em}
{\aclight\fontsize{12pt}{14pt}\selectfont\color{acpink}
A Cross-Platform Fighting-Game Laboratory for Aesthetic Computer}\par
@@ -226,7 +226,7 @@ Table~\ref{tab:endpoints} is the complete network surface used by the present bu
\begin{table*}[t]
\centering
-\caption{OSKIEWAR network endpoints. Braces denote validated variables; \texttt{match} is a pronounceable three-word name and \texttt{id} is prefixed \texttt{ow-}.}
+\caption{oskiewar network endpoints. Braces denote validated variables; \texttt{match} is a pronounceable three-word name and \texttt{id} is prefixed \texttt{ow-}.}
\label{tab:endpoints}
\small
\begin{tabularx}{\textwidth}{@{}p{0.19\textwidth}p{0.37\textwidth}X@{}}
@@ -272,7 +272,7 @@ Identity/HUD & @jeffrey, @oskie, @fifi, @sat, DUMMY; handle colors; MOTD/latest-
Sound & Spatial local synthesis; event OSC; Max for Live receiver; labeled sample-free Drum Rack; raw OSC bus. & UDP redundancy reduces loss but cannot mathematically guarantee delivery; receiver must observe sequence gaps.\\
Replay/spectator & Instant replay viewer; globally stored demos; match counts; public phone URL; QR; bounded latest-only relay. & Determinism across future physics versions requires migration or preservation of old simulation code.\\
Web & URL-addressable read-only spectator; interpolated fighters and balls; authoritative camera projection; live/viewer status; responsive handles beneath bodies. & Browser combat input and feature-equivalent simulation are not yet integrated.\\
-Distribution & Installed name OSKIEWAR; launcher chooses OSKIEWAR or NEW GAME input lab; package 1.0.0.35. & Self-signed Developer Mode package; Microsoft Store association and certification have not been completed.\\
+Distribution & Installed name oskiewar; launcher chooses oskiewar or NEW GAME input lab; package 1.0.0.35. & Self-signed Developer Mode package; Microsoft Store association and certification have not been completed.\\
\bottomrule
\end{tabularx}
\end{table*}
@@ -330,7 +330,7 @@ Microsoft, ``Windows.Gaming.Input,'' Windows App SDK documentation. \url{https:/
M. Wright, ``Open Sound Control: an enabling technology for musical networking,'' Center for New Music and Audio Technologies, University of California, Berkeley, 2005.
\bibitem{repo}
-Aesthetic Computer contributors, ``OSKIEWAR source, tests, native host, replay endpoint, spectator relay, web viewer, and Ableton devices,'' commit \code{ec3978b5b8}, 5 August 2026. \url{https://github.com/whistlegraph/aesthetic-computer}
+Aesthetic Computer contributors, ``oskiewar source, tests, native host, replay endpoint, spectator relay, web viewer, and Ableton devices,'' commit \code{ec3978b5b8}, 5 August 2026. \url{https://github.com/whistlegraph/aesthetic-computer}
\end{thebibliography}
\end{document}
diff --git a/papers/cli.mjs b/papers/cli.mjs
index 5ee4265e88..d063fa363a 100755
--- a/papers/cli.mjs
+++ b/papers/cli.mjs
@@ -303,7 +303,7 @@ const PAPER_MAP = {
"arxiv-oskiewar": {
base: "oskiewar",
siteName: "oskiewar-26-arxiv",
- title: "OSKIEWAR",
+ title: "oskiewar",
},
"arxiv-penrose": {
base: "penrose",
diff --git a/session-server/oskiewar-live-manager.mjs b/session-server/oskiewar-live-manager.mjs
index cf88772dda..e743861842 100644
--- a/session-server/oskiewar-live-manager.mjs
+++ b/session-server/oskiewar-live-manager.mjs
@@ -1,4 +1,4 @@
-// OSKIEWAR Live Manager, 26.08.04
+// oskiewar Live Manager, 26.08.04
// Small, public, match-id-scoped relay for phone spectators.
import {
diff --git a/session-server/session.mjs b/session-server/session.mjs
index fc317c4c20..ac5b99cf51 100644
--- a/session-server/session.mjs
+++ b/session-server/session.mjs
@@ -1726,7 +1726,7 @@ wss.on("connection", async (ws, req) => {
log('🔌 Total wss.clients.size:', wss.clients.size);
log('🔌 Current connections count:', Object.keys(connections).length);
- // Public, pronounceable-ID-scoped OSKIEWAR phone spectator rooms.
+ // Public, pronounceable-ID-scoped oskiewar phone spectator rooms.
if (oskiewarLiveManager.handleConnection(ws, req)) return;
// Route status dashboard WebSocket connections separately
diff --git a/system/public/aesthetic.computer/disks/oskiewar.mjs b/system/public/aesthetic.computer/disks/oskiewar.mjs
index 976d58e47c..ab80ef8824 100644
--- a/system/public/aesthetic.computer/disks/oskiewar.mjs
+++ b/system/public/aesthetic.computer/disks/oskiewar.mjs
@@ -1,5 +1,5 @@
-// OSKIEWAR, 26.08.05
-// Opens the standalone OSKIEWAR game, live room, or replay suite.
+// oskiewar, 26.08.05
+// Opens the standalone oskiewar game, live room, or replay suite.
const WORD = "(?:[bdfgklmnprstvz][aeiou]){3}";
const ROUND_NAME = new RegExp(`^(?:${WORD}-${WORD}-${WORD}|[a-z]{4,7}[0-9]{1,3})$`);
@@ -18,8 +18,8 @@ function boot({ colon = [], params = [], jump }) {
function meta({ params = [], colon = [] } = {}) {
const url = target({ params, colon });
const round = url.split("/").filter(Boolean).at(-1);
- return { title: round && round !== "oskiewar.com" ? `OSKIEWAR ${round}` : "OSKIEWAR",
- desc: "Play OSKIEWAR or open a live round and its demo." };
+ return { title: round && round !== "oskiewar.com" ? `oskiewar ${round}` : "oskiewar",
+ desc: "Play oskiewar or open a live round and its demo." };
}
export { boot, meta, target };
diff --git a/system/tests/oskiewar-analytics.test.mjs b/system/tests/oskiewar-analytics.test.mjs
index f035884d81..5ffb202fa3 100644
--- a/system/tests/oskiewar-analytics.test.mjs
+++ b/system/tests/oskiewar-analytics.test.mjs
@@ -7,7 +7,7 @@ import {
oskiewarReplayProperties,
} from "../public/aesthetic.computer/lib/oskiewar-analytics.mjs";
-test("OSKIEWAR events discard identifiers, handles, scores, and content", () => {
+test("oskiewar events discard identifiers, handles, scores, and content", () => {
assert.deepEqual(
oskiewarEvent("round_stored", {
source_system: "lith",
diff --git a/system/tests/oskiewar-live-viewer.test.mjs b/system/tests/oskiewar-live-viewer.test.mjs
index 88ee4601b8..5c2707f2ab 100644
--- a/system/tests/oskiewar-live-viewer.test.mjs
+++ b/system/tests/oskiewar-live-viewer.test.mjs
@@ -2,7 +2,7 @@ import test from "node:test";
import assert from "node:assert/strict";
import { boot, target } from "../public/aesthetic.computer/disks/oskiewar.mjs";
-test("the AC piece jumps out to the standalone OSKIEWAR suite", () => {
+test("the AC piece jumps out to the standalone oskiewar suite", () => {
const destinations = [];
boot({ jump: (url) => destinations.push(url) });
assert.deepEqual(destinations, ["out:https://oskiewar.com/"]);
diff --git a/system/tests/product-analytics.test.mjs b/system/tests/product-analytics.test.mjs
index cd53d7d8c1..324eb0dcf5 100644
--- a/system/tests/product-analytics.test.mjs
+++ b/system/tests/product-analytics.test.mjs
@@ -27,7 +27,7 @@ test("routes omit user-published names and source", () => {
assert.equal(safeRoutePath("/notepat?ignored=true"), "/_other");
});
-test("OSKIEWAR routes collapse round identifiers", () => {
+test("oskiewar routes collapse round identifiers", () => {
assert.equal(safeAnalyticsRoute("/", "oskiewar.com"), "/oskiewar");
assert.equal(
safeAnalyticsRoute("/bafegu-dorimi-kunapo", "oskiewar.com"),
diff --git a/xbox/live/OSKIEWAR-FRAME-DESIGN.md b/xbox/live/OSKIEWAR-FRAME-DESIGN.md
index 3c5cca9566..d34971bee1 100644
--- a/xbox/live/OSKIEWAR-FRAME-DESIGN.md
+++ b/xbox/live/OSKIEWAR-FRAME-DESIGN.md
@@ -1,4 +1,4 @@
-# OSKIEWAR pose and frame system
+# oskiewar pose and frame system
Target: original geometry-only fighters in `xbox/live/hello.js`, simulated at 60 Hz. Street Fighter references are timing and readability research only. Do not import, trace, ship, or derive coordinates from copyrighted sprites, captures, models, or hitbox images.
diff --git a/xbox/live/dannom.js b/xbox/live/dannom.js
index 71bf6686bc..8124fe488a 100644
--- a/xbox/live/dannom.js
+++ b/xbox/live/dannom.js
@@ -1,4 +1,4 @@
-// Dannom for the OSKIEWAR-generation native Xbox host. GENERATED.
+// Dannom for the oskiewar-generation native Xbox host. GENERATED.
// Rebuild: node xbox/tools/bundle-nom.mjs
(function (xboxNativeSynth) {
// source: system/public/aesthetic.computer/lib/percussion.mjs
@@ -3633,7 +3633,7 @@ function xboxNomOpaque(color) {
}
function xboxNomRect(x, y, width, height, style) {
- // The current OSKIEWAR host's box primitive is opaque. Dropping translucent
+ // The current oskiewar host's box primitive is opaque. Dropping translucent
// overlays preserves the board; drawing their blended color as an opaque
// rectangle would erase every command beneath it.
if (xboxNomInk[3] < 250) return;
@@ -3793,7 +3793,7 @@ globalThis.paint = function paint() {
nomPaint(xboxNomPaintApi);
};
-// OSKIEWAR samples controller state in its fixed simulation step too. Keeping
+// oskiewar samples controller state in its fixed simulation step too. Keeping
// act empty prevents a native edge callback from advancing Nom twice.
globalThis.act = function act() {};
globalThis.leave = function leave() { nomLeave(); };
diff --git a/xbox/live/hello.js b/xbox/live/hello.js
index 7a68338b0a..b603ff5fe3 100644
--- a/xbox/live/hello.js
+++ b/xbox/live/hello.js
@@ -1,5 +1,5 @@
// @bundle-qr
-const buildTimestamp = "2026.08.07.1251 PDT";
+const buildTimestamp = "2026.08.07.1336 PDT";
const floorY = 12000;
const ceilingY = 0;
const wallThickness = 80;
@@ -4810,6 +4810,17 @@ function animatedTitleColor(index, t, daylight = visualTheme.light) {
return mixColor(night, day, daylight);
}
+function titleButtonRect() {
+ const compact = compactLayout();
+ const textSize = hudTypeSize;
+ const textWidth = handleWidth("start", textSize);
+ const width = textWidth + (compact ? 54 : 76);
+ const height = textSize + (compact ? 24 : 34);
+ return { x: viewCenterX() - width / 2,
+ y: viewHeight * (compact ? .61 : .64) - (compact ? 10 : 15),
+ width, height, textSize, textWidth };
+}
+
function drawTitleScreen(t, ink, transitionAge = -1) {
const compact = compactLayout();
const socialPreview = typeof capabilities === "function" &&
@@ -4852,14 +4863,29 @@ function drawTitleScreen(t, ink, transitionAge = -1) {
}
const prompt = "start";
- const promptSize = hudTypeSize;
- const promptWidth = handleWidth(prompt, promptSize);
+ const button = titleButtonRect();
+ const pointer = globalThis.__oskiewarTouch?.pointer;
+ const hovered = transitionAge < 0 && pointer?.active && pointInRect(pointer, button);
+ if (globalThis.__oskiewarTouch) {
+ globalThis.__oskiewarTouch.titleButton = button;
+ globalThis.__oskiewarTouch.titleHover = Boolean(hovered);
+ }
const promptPulse = .68 + (Math.sin(t * 3.2) + 1) * .16;
const promptInk = transitionInk ||
mixColor([196, 142, 18], [255, 238, 82], promptPulse);
- if (!socialPreview && (transitionAge >= 0 || Math.floor(t * 2.4) % 2 === 0))
- typeWrite(prompt, viewCenterX() - promptWidth / 2,
- viewHeight * (compact ? .61 : .64), promptSize, ...promptInk);
+ if (!socialPreview) {
+ const buttonPanel = transitionInk || mixColor(
+ [18, 24, 48], [215, 225, 239], visualTheme.light);
+ const buttonEdge = hovered ? promptInk : mixColor(buttonPanel, ink, .24);
+ box(button.x + 5, button.y + 6, button.width, button.height,
+ ...mixColor([3, 5, 14], [96, 104, 118], visualTheme.light));
+ box(button.x, button.y, button.width, button.height,
+ ...mixColor(buttonPanel, promptInk, hovered ? .25 : .06));
+ box(button.x, button.y, button.width, hovered ? 7 : 4, ...buttonEdge);
+ typeWrite(prompt, button.x + (button.width - button.textWidth) / 2,
+ button.y + (button.height - button.textSize) / 2 - 2,
+ button.textSize, ...promptInk);
+ }
if (transitionAge >= 0 || socialPreview) return;
const titleNow = pacificTimeLabel(runtime().unixMs || Date.now());
const stamp = buildTimestamp.match(/^(\d{4})\.(\d{2})\.(\d{2})\.(\d{2})(\d{2})/);
diff --git a/xbox/live/mac-test.html b/xbox/live/mac-test.html
index 70680b570a..6593def012 100644
--- a/xbox/live/mac-test.html
+++ b/xbox/live/mac-test.html
@@ -4,7 +4,7 @@
oskiewar
-
+
@@ -37,7 +37,7 @@
@supports (height: 100dvh) { html, body { height: 100dvh; } }
body { display: grid; place-items: center; font-family: "Comic Relief", "Comic Sans MS", Arial, sans-serif;
cursor: url("/aesthetic.computer/cursors/precise.svg") 12 12, auto; }
- body.selection-hover, body.title-open {
+ body.selection-hover {
cursor: url("/aesthetic.computer/cursors/active.svg") 12 12, pointer; }
canvas { position: fixed; inset: 0; width: 100vw; height: 100vh;
min-width: 0; min-height: 0; display: block; cursor: inherit; }
@@ -98,7 +98,7 @@
-
+
@@ -225,8 +225,18 @@
return true;
}
- function tapTitle() {
- if (!dismissTitle()) return false;
+ function tapTitle(point = null) {
+ if (!titleOpen) return false;
+ const button = globalThis.__oskiewarTouch?.titleButton;
+ const inside = !point || button && point.x >= button.x &&
+ point.x <= button.x + button.width && point.y >= button.y &&
+ point.y <= button.y + button.height;
+ if (!inside) {
+ ensureAudio();
+ drum("block", .32, 0);
+ return true;
+ }
+ dismissTitle();
keys[0].add("A");
ensureAudio();
globalThis.__oskiewarSfx?.unlock().catch(() => {});
@@ -604,11 +614,11 @@
globalThis.__oskiewarTouch.pointer.active = false;
});
canvas.addEventListener("pointerdown", (event) => {
- if (tapTitle()) {
+ const point = canvasPoint(event);
+ if (tapTitle(point)) {
event.preventDefault();
return;
}
- const point = canvasPoint(event);
Object.assign(globalThis.__oskiewarTouch.pointer, point, { active: true });
selectTaps.push(point);
ensureAudio();
@@ -727,7 +737,8 @@
}
previousPieceScreen = pieceScreen;
const hover = globalThis.__oskiewarTouch?.hover || null;
- document.body.classList.toggle("selection-hover", Boolean(hover));
+ const titleHover = globalThis.__oskiewarTouch?.titleHover === true;
+ document.body.classList.toggle("selection-hover", Boolean(hover) || titleHover);
const key = hover ? [hover.back ? "back" : "", hover.option ?? ""].join(":") : "";
if (key && key !== selectionHoverSoundKey && !titleOpen)
oskiewarSfx.drum("hat", .26, 0);
diff --git a/xbox/live/nom-host-adapter.js b/xbox/live/nom-host-adapter.js
index a137fdc609..26899d476a 100644
--- a/xbox/live/nom-host-adapter.js
+++ b/xbox/live/nom-host-adapter.js
@@ -42,7 +42,7 @@ function xboxNomOpaque(color) {
}
function xboxNomRect(x, y, width, height, style) {
- // The current OSKIEWAR host's box primitive is opaque. Dropping translucent
+ // The current oskiewar host's box primitive is opaque. Dropping translucent
// overlays preserves the board; drawing their blended color as an opaque
// rectangle would erase every command beneath it.
if (xboxNomInk[3] < 250) return;
@@ -202,7 +202,7 @@ globalThis.paint = function paint() {
nomPaint(xboxNomPaintApi);
};
-// OSKIEWAR samples controller state in its fixed simulation step too. Keeping
+// oskiewar samples controller state in its fixed simulation step too. Keeping
// act empty prevents a native edge callback from advancing Nom twice.
globalThis.act = function act() {};
globalThis.leave = function leave() { nomLeave(); };
diff --git a/xbox/live/nopaint.html b/xbox/live/nopaint.html
new file mode 100644
index 0000000000..23763c1d3a
--- /dev/null
+++ b/xbox/live/nopaint.html
@@ -0,0 +1,219 @@
+
+
+
+
+
+
No Paint Xbox — command prototype
+
+
+
+
+
+
+
+
+
+
diff --git a/xbox/live/nopaint.js b/xbox/live/nopaint.js
new file mode 100644
index 0000000000..c6caa516b5
--- /dev/null
+++ b/xbox/live/nopaint.js
@@ -0,0 +1,633 @@
+// @bundle-qr
+// No Paint Xbox prototype: command-backed paintings.
+
+const background = [247, 243, 232];
+const inkColors = [
+ [24, 27, 35],
+ [238, 70, 101],
+ [30, 124, 214],
+ [244, 172, 42],
+ [57, 170, 116],
+ [133, 82, 186],
+];
+const uiInk = [24, 27, 35];
+const uiPaper = [247, 243, 232];
+const uiRule = [205, 198, 181];
+const noHover = [238, 70, 101];
+const paintHover = [30, 124, 214];
+const acceptedMarks = [];
+const brushKinds = ["line", "box", "triangle", "rays", "echo", "grid"];
+const effectKinds = ["mirror", "turn", "zoom", "blur", "invert",
+ "flip", "scroll", "skew", "breathe", "saturate", "contrast",
+ "posterize", "recurse", "vignette", "noise"];
+const proposalKinds = [...brushKinds, ...effectKinds];
+const acceptedEffects = [];
+const maxPaintingCommands = 1600;
+
+let proposal = null;
+let proposalNumber = 0;
+let rejectedCount = 0;
+let mode = "making";
+let previousButtons = [];
+let randomState = 1;
+let view = { width: 1920, height: 1080 };
+let proposalFrame = 0;
+let decisionHeld = false;
+
+function hashSeed(value) {
+ let hash = 2166136261;
+ for (const character of String(value)) {
+ hash ^= character.charCodeAt(0);
+ hash = Math.imul(hash, 16777619);
+ }
+ return hash >>> 0 || 1;
+}
+
+function random() {
+ randomState ^= randomState << 13;
+ randomState ^= randomState >>> 17;
+ randomState ^= randomState << 5;
+ return (randomState >>> 0) / 4294967296;
+}
+
+function clamp(value, low, high) {
+ return Math.max(low, Math.min(high, value));
+}
+
+function syncView() {
+ const next = typeof gameView === "function" ? gameView() : null;
+ view.width = clamp(Math.round(Number(next?.width) || 1920), 480, 3840);
+ view.height = clamp(Math.round(Number(next?.height) || 1080), 480, 2160);
+}
+
+function ink() {
+ return inkColors[Math.floor(random() * inkColors.length)].slice();
+}
+
+function lineCommand(x1, y1, x2, y2, width, color) {
+ return { primitive: "line", x1, y1, x2, y2, width, color: color.slice() };
+}
+
+function makeLine() {
+ const angle = random() * Math.PI * 2;
+ const length = .16 + random() * .58;
+ const centerX = .14 + random() * .72;
+ const centerY = .13 + random() * .62;
+ const dx = Math.cos(angle) * length * .5;
+ const dy = Math.sin(angle) * length * .5;
+ return [lineCommand(clamp(centerX - dx, .055, .945),
+ clamp(centerY - dy, .055, .78), clamp(centerX + dx, .055, .945),
+ clamp(centerY + dy, .055, .78), .004 + random() * .022, ink())];
+}
+
+function makeBox() {
+ const width = .09 + random() * .32;
+ const height = .07 + random() * .28;
+ return [{ primitive: "box", x: .06 + random() * (.88 - width),
+ y: .06 + random() * (.72 - height), width, height, color: ink() }];
+}
+
+function makeTriangle() {
+ const centerX = .18 + random() * .64;
+ const centerY = .16 + random() * .48;
+ const radius = .08 + random() * .18;
+ const angle = random() * Math.PI * 2;
+ const points = [0, 1, 2].map((index) => {
+ const theta = angle + index * Math.PI * 2 / 3;
+ return { x: clamp(centerX + Math.cos(theta) * radius, .04, .96),
+ y: clamp(centerY + Math.sin(theta) * radius, .04, .79) };
+ });
+ return [{ primitive: "triangle", x1: points[0].x, y1: points[0].y,
+ x2: points[1].x, y2: points[1].y, x3: points[2].x, y3: points[2].y,
+ color: ink() }];
+}
+
+function makeRays() {
+ const centerX = .18 + random() * .64;
+ const centerY = .16 + random() * .48;
+ const radius = .10 + random() * .20;
+ const count = 7 + Math.floor(random() * 7);
+ const color = ink();
+ const width = .003 + random() * .009;
+ const commands = [];
+ for (let index = 0; index < count; index++) {
+ const angle = index / count * Math.PI * 2 + random() * .08;
+ const reach = radius * (.72 + random() * .36);
+ commands.push(lineCommand(centerX, centerY,
+ clamp(centerX + Math.cos(angle) * reach, .035, .965),
+ clamp(centerY + Math.sin(angle) * reach, .035, .79), width, color));
+ }
+ return commands;
+}
+
+function makeEcho() {
+ const color = ink();
+ const angle = random() * Math.PI * 2;
+ const length = .18 + random() * .38;
+ const centerX = .22 + random() * .56;
+ const centerY = .18 + random() * .42;
+ const dx = Math.cos(angle) * length * .5;
+ const dy = Math.sin(angle) * length * .5;
+ const nx = -Math.sin(angle) * .025;
+ const ny = Math.cos(angle) * .025;
+ return [-2, -1, 0, 1, 2].map((offset) => lineCommand(
+ clamp(centerX - dx + nx * offset, .035, .965),
+ clamp(centerY - dy + ny * offset, .035, .79),
+ clamp(centerX + dx + nx * offset, .035, .965),
+ clamp(centerY + dy + ny * offset, .035, .79),
+ .003 + Math.abs(offset) * .0015, color));
+}
+
+function makeGrid() {
+ const color = ink();
+ const left = .08 + random() * .52;
+ const top = .08 + random() * .38;
+ const width = .18 + random() * Math.min(.34, .89 - left);
+ const height = .14 + random() * Math.min(.27, .76 - top);
+ const columns = 2 + Math.floor(random() * 4);
+ const rows = 2 + Math.floor(random() * 4);
+ const commands = [];
+ for (let column = 0; column <= columns; column++) {
+ const x = left + width * column / columns;
+ commands.push(lineCommand(x, top, x, top + height, .0025, color));
+ }
+ for (let row = 0; row <= rows; row++) {
+ const y = top + height * row / rows;
+ commands.push(lineCommand(left, y, left + width, y, .0025, color));
+ }
+ return commands;
+}
+
+function nextProposal() {
+ proposalNumber++;
+ const kind = proposalKinds[(proposalNumber - 1) % proposalKinds.length];
+ const factories = { line: makeLine, box: makeBox, triangle: makeTriangle,
+ rays: makeRays, echo: makeEcho, grid: makeGrid };
+ proposal = effectKinds.includes(kind)
+ ? { kind, family: "effect", effect: { kind,
+ seed: Math.floor(random() * 1000000) } }
+ : { kind, family: "brush", commands: factories[kind]() };
+ proposalFrame = 0;
+}
+
+function drawCommand(command) {
+ const scale = Math.min(view.width, view.height);
+ if (command.primitive === "line") {
+ line(command.x1 * view.width, command.y1 * view.height,
+ command.x2 * view.width, command.y2 * view.height,
+ Math.max(2, command.width * scale), ...command.color);
+ } else if (command.primitive === "box") {
+ box(command.x * view.width, command.y * view.height,
+ command.width * view.width, command.height * view.height, ...command.color);
+ } else if (command.primitive === "triangle") {
+ triangle(command.x1 * view.width, command.y1 * view.height,
+ command.x2 * view.width, command.y2 * view.height,
+ command.x3 * view.width, command.y3 * view.height, ...command.color);
+ }
+}
+
+function drawMark(mark) {
+ for (const command of mark.commands) drawCommand(command);
+}
+
+function cloneMark(mark) {
+ return { kind: mark.kind, family: mark.family || "brush",
+ ...(mark.effect ? { effect: { ...mark.effect } } : {}),
+ ...(mark.commands ? { commands: mark.commands.map((command) => ({
+ ...command, color: command.color.slice(),
+ })) } : {}) };
+}
+
+function mix(from, to, amount) {
+ return from + (to - from) * amount;
+}
+
+function animatedCommand(command, index, frame) {
+ const phase = frame / 18 + index * .71;
+ const dx = Math.sin(phase) * .006;
+ const dy = Math.cos(phase * .83) * .005;
+ const pulse = 1 + Math.sin(phase * .73) * .035;
+ const color = command.color.map((value, channel) => channel < 3
+ ? clamp(Math.round(value + Math.sin(phase + channel) * 9), 0, 255) : value);
+ if (command.primitive === "line") return { ...command,
+ x1: clamp(command.x1 + dx, -.1, 1.1),
+ y1: clamp(command.y1 + dy, -.1, 1.1),
+ x2: clamp(command.x2 - dx * .55, -.1, 1.1),
+ y2: clamp(command.y2 - dy * .55, -.1, 1.1),
+ width: command.width * pulse, color };
+ if (command.primitive === "box") return { ...command,
+ x: command.x + dx, y: command.y + dy,
+ width: command.width * pulse, height: command.height / pulse, color };
+ return { ...command,
+ x1: command.x1 + dx, y1: command.y1 + dy,
+ x2: command.x2 - dy, y2: command.y2 + dx,
+ x3: command.x3 - dx, y3: command.y3 - dy, color };
+}
+
+function animatedMark(mark, frame = proposalFrame) {
+ return { kind: mark.kind, family: "brush",
+ commands: mark.commands.map((command, index) =>
+ animatedCommand(command, index, frame)) };
+}
+
+function transformCommand(command, transform, colorTransform = (color) => color) {
+ const color = colorTransform(command.color.slice());
+ if (command.primitive === "line") {
+ const a = transform(command.x1, command.y1);
+ const b = transform(command.x2, command.y2);
+ return [{ ...command, x1: a.x, y1: a.y, x2: b.x, y2: b.y, color }];
+ }
+ if (command.primitive === "triangle") {
+ const a = transform(command.x1, command.y1);
+ const b = transform(command.x2, command.y2);
+ const c = transform(command.x3, command.y3);
+ return [{ ...command, x1: a.x, y1: a.y, x2: b.x, y2: b.y,
+ x3: c.x, y3: c.y, color }];
+ }
+ const a = transform(command.x, command.y);
+ const b = transform(command.x + command.width, command.y);
+ const c = transform(command.x + command.width, command.y + command.height);
+ const d = transform(command.x, command.y + command.height);
+ return [
+ { primitive: "triangle", x1: a.x, y1: a.y, x2: b.x, y2: b.y,
+ x3: c.x, y3: c.y, color: color.slice() },
+ { primitive: "triangle", x1: a.x, y1: a.y, x2: c.x, y2: c.y,
+ x3: d.x, y3: d.y, color: color.slice() },
+ ];
+}
+
+function transformMarks(marks, transform, colorTransform) {
+ return marks.map((mark) => ({ kind: mark.kind, family: "brush",
+ commands: mark.commands.flatMap((command) =>
+ transformCommand(command, transform, colorTransform)) }));
+}
+
+function supportsOrderedEffects() {
+ return typeof capabilities === "function" &&
+ capabilities()?.orderedEffects === true;
+}
+
+function softenMarks(marks, frame = proposalFrame) {
+ const spread = .002 + blurRadius(frame) * .0007;
+ const offsets = [[-1, 0], [1, 0], [0, -1], [0, 1]];
+ let remaining = maxPaintingCommands;
+ return marks.map((mark) => {
+ const commands = [];
+ for (const command of mark.commands) {
+ for (const [x, y] of offsets) {
+ const ghost = command.color.map((value, channel) => channel < 3
+ ? Math.round(mix(value, background[channel], .62)) : value);
+ commands.push(...transformCommand(command,
+ (px, py) => ({ x: px + x * spread, y: py + y * spread }), () => ghost));
+ }
+ commands.push({ ...command, color: command.color.slice() });
+ if (commands.length >= remaining) break;
+ }
+ const kept = commands.slice(0, remaining);
+ remaining -= kept.length;
+ return { kind: mark.kind, family: "brush", commands: kept };
+ });
+}
+
+function applyEffect(marks, effect, frame = proposalFrame) {
+ const phase = frame / 24;
+ if (effect.kind === "mirror") {
+ const scaleX = Math.cos(phase * .9);
+ return transformMarks(marks,
+ (x, y) => ({ x: .5 + (x - .5) * scaleX, y }));
+ }
+ if (effect.kind === "turn") {
+ const angle = Math.sin(phase * .7) * Math.PI * .32;
+ const cosine = Math.cos(angle);
+ const sine = Math.sin(angle);
+ return transformMarks(marks, (x, y) => ({
+ x: .5 + (x - .5) * cosine - (y - .42) * sine,
+ y: .42 + (x - .5) * sine + (y - .42) * cosine,
+ }));
+ }
+ if (effect.kind === "flip") {
+ const scaleY = Math.cos(phase * .9);
+ return transformMarks(marks,
+ (x, y) => ({ x, y: .42 + (y - .42) * scaleY }));
+ }
+ if (effect.kind === "zoom") {
+ const scale = 1 + Math.sin(phase * .8) * .24;
+ return transformMarks(marks,
+ (x, y) => ({ x: .5 + (x - .5) * scale, y: .42 + (y - .42) * scale }));
+ }
+ if (effect.kind === "scroll") {
+ const dx = Math.sin(phase * .72) * .22;
+ const dy = Math.cos(phase * .61) * .12;
+ return transformMarks(marks, (x, y) => ({ x: x + dx, y: y + dy }));
+ }
+ if (effect.kind === "skew") {
+ const amount = Math.sin(phase * .8) * .62;
+ return transformMarks(marks,
+ (x, y) => ({ x: x + (y - .42) * amount, y }));
+ }
+ if (effect.kind === "breathe") {
+ const pulse = Math.sin(phase * .85);
+ return transformMarks(marks, (x, y) => ({
+ x: .5 + (x - .5) * (1 + pulse * .18),
+ y: .42 + (y - .42) * (1 - pulse * .12),
+ }));
+ }
+ if (effect.kind === "invert") {
+ const amount = .5 + Math.sin(phase) * .5;
+ return transformMarks(marks, (x, y) => ({ x, y }), (color) =>
+ color.map((value, channel) => channel < 3
+ ? Math.round(mix(value, 255 - value, amount)) : value));
+ }
+ if (effect.kind === "saturate") {
+ const amount = .35 + (.5 + Math.sin(phase) * .5) * 2.3;
+ return transformMarks(marks, (x, y) => ({ x, y }), (color) => {
+ const gray = color[0] * .299 + color[1] * .587 + color[2] * .114;
+ return color.map((value, channel) => channel < 3
+ ? clamp(Math.round(gray + (value - gray) * amount), 0, 255) : value);
+ });
+ }
+ if (effect.kind === "contrast") {
+ const amount = .55 + (.5 + Math.sin(phase * .82) * .5) * 1.7;
+ return transformMarks(marks, (x, y) => ({ x, y }), (color) =>
+ color.map((value, channel) => channel < 3
+ ? clamp(Math.round(128 + (value - 128) * amount), 0, 255) : value));
+ }
+ if (effect.kind === "posterize") {
+ const levels = 2 + Math.round((.5 + Math.sin(phase * .7) * .5) * 5);
+ return transformMarks(marks, (x, y) => ({ x, y }), (color) =>
+ color.map((value, channel) => channel < 3
+ ? Math.round(Math.round(value / 255 * (levels - 1)) * 255 / (levels - 1))
+ : value));
+ }
+ if (effect.kind === "recurse") {
+ const scale = .38 + Math.sin(phase * .63) * .07;
+ const centers = [[.25, .21], [.75, .21], [.25, .63], [.75, .63]];
+ return marks.map((mark) => ({ kind: mark.kind, family: "brush",
+ commands: centers.flatMap(([cx, cy]) => mark.commands.flatMap((command) =>
+ transformCommand(command, (x, y) => ({
+ x: cx + (x - .5) * scale, y: cy + (y - .42) * scale,
+ })))).slice(0, maxPaintingCommands) }));
+ }
+ if (effect.kind === "vignette") {
+ const depth = .035 + (.5 + Math.sin(phase) * .5) * .08;
+ const shade = Math.round(34 + (.5 + Math.cos(phase) * .5) * 42);
+ return [...marks.map(cloneMark), { kind: "vignette", family: "brush", commands: [
+ { primitive: "box", x: 0, y: 0, width: 1, height: depth,
+ color: [shade, shade, shade] },
+ { primitive: "box", x: 0, y: .84 - depth, width: 1, height: depth,
+ color: [shade, shade, shade] },
+ { primitive: "box", x: 0, y: depth, width: depth, height: .84 - depth * 2,
+ color: [shade, shade, shade] },
+ { primitive: "box", x: 1 - depth, y: depth, width: depth,
+ height: .84 - depth * 2, color: [shade, shade, shade] },
+ ] }];
+ }
+ if (effect.kind === "noise") {
+ const unit = (index, axis) => {
+ const value = Math.sin(effect.seed * .013 + index * 78.233 + axis * 31.177) *
+ 43758.5453;
+ return value - Math.floor(value);
+ };
+ const commands = Array.from({ length: 96 }, (_, index) => {
+ const drift = Math.sin(phase + index * .37) * .006;
+ const value = Math.round(24 + unit(index, 2) * 207);
+ const size = .002 + unit(index, 3) * .009;
+ return { primitive: "box", x: unit(index, 0) + drift,
+ y: unit(index, 1) * .84 - drift, width: size, height: size,
+ color: [value, value, value] };
+ });
+ return [...marks.map(cloneMark), { kind: "noise", family: "brush", commands }];
+ }
+ return marks.map(cloneMark);
+}
+
+function blurRadius(frame = proposalFrame) {
+ return 2 + Math.round((.5 + Math.sin(frame / 24) * .5) * 8);
+}
+
+function applyRenderEffect(effect) {
+ if (effect.kind === "blur" && supportsOrderedEffects() &&
+ typeof blur === "function") blur(effect.radius);
+}
+
+// This is the complete painting compositor. It deliberately knows nothing
+// about proposals, buttons, labels, or progress UI.
+function renderMarks(marks, effects = acceptedEffects) {
+ wipe(...background);
+ const ordered = effects.slice().sort((a, b) => a.after - b.after);
+ let effectIndex = 0;
+ while (effectIndex < ordered.length && ordered[effectIndex].after === 0)
+ applyRenderEffect(ordered[effectIndex++]);
+ for (let index = 0; index < marks.length; index++) {
+ drawMark(marks[index]);
+ while (effectIndex < ordered.length && ordered[effectIndex].after === index + 1)
+ applyRenderEffect(ordered[effectIndex++]);
+ }
+}
+
+function renderPainting() {
+ renderMarks(acceptedMarks);
+}
+
+function controlLocale() {
+ const device = typeof capabilities === "function" ? capabilities() || {} : {};
+ const platform = String(device.platform || "").toLowerCase();
+ const family = String(device.inputFamily || "").toLowerCase();
+ if (family === "touch") return {
+ no: "TAP NO", paint: "TAP PAINT", done: "TAP DONE",
+ noKey: "TAP", paintKey: "TAP", doneKey: "TAP",
+ noAction: "No", paintAction: "Paint", doneAction: "Done", family: "touch",
+ };
+ if (family === "xbox" || platform.includes("xbox")) return {
+ no: "LEFT NO", paint: "RIGHT PAINT", done: "MENU DONE",
+ noKey: "LEFT", paintKey: "RIGHT", doneKey: "MENU",
+ noAction: "No", paintAction: "Paint", doneAction: "Done", family: "xbox",
+ };
+ if (platform === "web") return {
+ no: "B NO", paint: "A PAINT", done: "RETURN DONE",
+ noKey: "B", paintKey: "A", doneKey: "RETURN",
+ noAction: "No", paintAction: "Paint", doneAction: "Done", family: "web",
+ };
+ return {
+ no: "LEFT NO", paint: "RIGHT PAINT", done: "RETURN DONE",
+ noKey: "LEFT", paintKey: "RIGHT", doneKey: "RETURN",
+ noAction: "No", paintAction: "Paint", doneAction: "Done", family: "keyboard",
+ };
+}
+
+function uiLayout() {
+ const compact = view.width < view.height * .8;
+ const height = compact ? Math.max(210, Math.round(view.height * .20))
+ : Math.max(140, Math.round(view.height * .15));
+ const top = view.height - height;
+ const pad = Math.max(18, Math.round(view.width * .018));
+ const headerHeight = 0;
+ const buttonTop = top;
+ const buttonHeight = Math.max(56, view.height - buttonTop);
+ const buttonWidth = view.width / 2;
+ return { compact, height, top, pad, headerHeight,
+ no: { x: 0, y: buttonTop, width: buttonWidth, height: buttonHeight },
+ paint: { x: buttonWidth, y: buttonTop,
+ width: buttonWidth, height: buttonHeight } };
+}
+
+function pointInRect(x, y, rect) {
+ return x >= rect.x && x <= rect.x + rect.width &&
+ y >= rect.y && y <= rect.y + rect.height;
+}
+
+function hitTest(x, y) {
+ const layout = uiLayout();
+ if (pointInRect(x, y, layout.no)) return "B";
+ if (pointInRect(x, y, layout.paint)) return "A";
+ return null;
+}
+
+function hoveredButton() {
+ if (typeof pointer !== "function") return null;
+ const cursor = pointer() || {};
+ if (cursor.inside === false) return null;
+ const hit = hitTest(Number(cursor.x), Number(cursor.y));
+ return hit === "A" || hit === "B" ? hit : null;
+}
+
+function drawDecisionButton(rect, action, active, fill) {
+ const border = Math.max(3, Math.round(view.height * .004));
+ const x = rect.x;
+ const y = rect.y;
+ const height = rect.height;
+ box(x, y, rect.width, height, ...uiInk);
+ box(x + border, y + border, rect.width - border * 2, height - border * 2,
+ ...(active ? fill : uiPaper));
+ const inkColor = active ? [255, 255, 255] : uiInk;
+ const actionSize = Math.max(38, Math.round(view.height * .052));
+ const textWidth = (text, size) => String(text).length * size * .61;
+ const centeredX = (text, size) => x + (rect.width - textWidth(text, size)) / 2;
+ const centeredY = y + (height - actionSize) / 2;
+ write(action, centeredX(action, actionSize), centeredY, actionSize, ...inkColor);
+}
+
+function drawMakingUi() {
+ const layout = uiLayout();
+ const { pad } = layout;
+ const small = Math.max(22, Math.round(view.height * .027));
+ const prompt = proposal.kind.toUpperCase();
+ const edge = Math.max(1, Math.round(view.height * .002));
+ for (const [x, y] of [[-edge, 0], [edge, 0], [0, -edge], [0, edge]])
+ write(prompt, pad + x, pad + y, small, 255, 255, 255);
+ write(prompt, pad, pad, small, ...uiInk);
+ const controls = controlLocale();
+ const hover = hoveredButton();
+ drawDecisionButton(layout.no, controls.noAction,
+ hover === "B" || previousButtons.includes("B") ||
+ previousButtons.includes("ArrowLeft"), noHover);
+ drawDecisionButton(layout.paint, controls.paintAction,
+ hover === "A" || previousButtons.includes("A") ||
+ previousButtons.includes("ArrowRight"), paintHover);
+}
+
+function acceptProposal() {
+ if (mode !== "making" || !proposal) return;
+ if (proposal.family === "effect") {
+ if (proposal.effect.kind === "blur") {
+ if (supportsOrderedEffects()) {
+ acceptedEffects.push({ kind: "blur", after: acceptedMarks.length,
+ radius: blurRadius() });
+ } else {
+ const softened = softenMarks(acceptedMarks);
+ acceptedMarks.splice(0, acceptedMarks.length, ...softened);
+ }
+ } else {
+ const transformed = applyEffect(acceptedMarks, proposal.effect);
+ acceptedMarks.splice(0, acceptedMarks.length, ...transformed);
+ }
+ } else {
+ acceptedMarks.push(animatedMark(proposal));
+ }
+ nextProposal();
+}
+
+function rejectProposal() {
+ if (mode !== "making") return;
+ rejectedCount++;
+ nextProposal();
+}
+
+function toggleMode() {
+ mode = mode === "making" ? "finished" : "making";
+}
+
+function boot() {
+ syncView();
+ const seed = typeof nopaintSeed === "string"
+ ? nopaintSeed : "nopaint-xbox-line-v1";
+ randomState = hashSeed(seed);
+ acceptedMarks.length = 0;
+ acceptedEffects.length = 0;
+ proposalNumber = 0;
+ rejectedCount = 0;
+ mode = "making";
+ previousButtons = [];
+ proposalFrame = 0;
+ decisionHeld = false;
+ nextProposal();
+}
+
+function sim() {
+ syncView();
+ const down = typeof gamepad === "function" ? gamepad(0)?.down || [] : [];
+ const pressed = (button) => down.includes(button) && !previousButtons.includes(button);
+ const groupDown = (buttons, values = down) => buttons.some((button) =>
+ values.includes(button));
+ const released = (buttons) => !groupDown(buttons) && groupDown(buttons, previousButtons);
+ const noButtons = ["ArrowLeft", "B"];
+ const paintButtons = ["ArrowRight", "A"];
+ const paintingButtons = ["Canvas"];
+ if (pressed("Menu") || pressed("View")) toggleMode();
+ else if (released(paintingButtons)) toggleMode();
+ else if (released(paintButtons))
+ mode === "finished" ? toggleMode() : acceptProposal();
+ else if (released(noButtons) && mode === "making") rejectProposal();
+ decisionHeld = mode === "making" &&
+ (groupDown(noButtons) || groupDown(paintButtons) || groupDown(paintingButtons));
+ if (mode === "making" && !decisionHeld) proposalFrame++;
+ previousButtons = down.slice();
+}
+
+function paint() {
+ if (mode === "making" && proposal.family === "effect") {
+ if (proposal.effect.kind === "blur") {
+ if (supportsOrderedEffects()) {
+ renderMarks(acceptedMarks, [...acceptedEffects,
+ { kind: "blur", after: acceptedMarks.length, radius: blurRadius() }]);
+ } else renderMarks(softenMarks(acceptedMarks));
+ } else renderMarks(applyEffect(acceptedMarks, proposal.effect));
+ } else renderPainting();
+ if (mode !== "making") return;
+ if (proposal.family === "brush") drawMark(animatedMark(proposal));
+ drawMakingUi();
+}
+
+function act(button) {
+ // The native host also sends edge events. Polling gamepad() is authoritative,
+ // as it is in oskiewar, so act remains side-effect free.
+ void button;
+}
+
+function leave() {}
+
+function snapshot() {
+ return {
+ mode,
+ proposalNumber,
+ proposalFrame,
+ decisionHeld,
+ rejectedCount,
+ accepted: acceptedMarks.map(cloneMark),
+ effects: acceptedEffects.map((effect) => ({ ...effect })),
+ proposal: proposal ? cloneMark(proposal) : null,
+ controls: controlLocale(),
+ hover: hoveredButton(),
+ view: { ...view },
+ };
+}
diff --git a/xbox/live/oskiewar-sfx.mjs b/xbox/live/oskiewar-sfx.mjs
index 5593083e2c..796e2eef17 100644
--- a/xbox/live/oskiewar-sfx.mjs
+++ b/xbox/live/oskiewar-sfx.mjs
@@ -1,4 +1,4 @@
-// OSKIEWAR procedural SFX mock bank.
+// oskiewar procedural SFX mock bank.
//
// The short layered percussion voices follow Aesthetic Computer's shared
// notepat kit (`system/public/aesthetic.computer/lib/percussion.mjs`): tiny
diff --git a/xbox/live/render-social-preview.mjs b/xbox/live/render-social-preview.mjs
index 7c6dd18b8b..d86beb9840 100644
--- a/xbox/live/render-social-preview.mjs
+++ b/xbox/live/render-social-preview.mjs
@@ -1,4 +1,4 @@
-// Burn OSKIEWAR's real title renderer into Open Graph poster/video assets.
+// Burn oskiewar's real title renderer into Open Graph poster/video assets.
import { createHash } from "node:crypto";
import { spawnSync } from "node:child_process";
diff --git a/xbox/live/round-room.mjs b/xbox/live/round-room.mjs
index c74fd005f9..cfc79d4a27 100644
--- a/xbox/live/round-room.mjs
+++ b/xbox/live/round-room.mjs
@@ -1,4 +1,4 @@
-// OSKIEWAR round room transport, 26.08.05
+// oskiewar round room transport, 26.08.05
// Keeps live and stored-demo delivery outside the shared game/render engine.
const WORD = "(?:[bdfgklmnprstvz][aeiou]){3}";
@@ -15,7 +15,7 @@ export class RoundRoom {
analytics = () => false,
sessionOrigin = "wss://session-server.aesthetic.computer",
replayOrigin = "" } = {}) {
- if (!ROUND_NAME.test(name || "")) throw new Error("Invalid OSKIEWAR round name");
+ if (!ROUND_NAME.test(name || "")) throw new Error("Invalid oskiewar round name");
this.name = name;
this.WebSocketImpl = WebSocketImpl;
this.fetchImpl = typeof fetchImpl === "function"
diff --git a/xbox/live/social/manifest.json b/xbox/live/social/manifest.json
index 58068d5f95..5a8d8a67dc 100644
--- a/xbox/live/social/manifest.json
+++ b/xbox/live/social/manifest.json
@@ -1,7 +1,7 @@
{
"format": "ac.oskiewar.social-preview",
"version": 1,
- "build": "4719026132a77265",
+ "build": "0dd3398b7934b309",
"width": 1200,
"height": 630,
"durationSeconds": 6,
diff --git a/xbox/live/social/oskiewar-title.jpg b/xbox/live/social/oskiewar-title.jpg
index 3756ef979716bb9d219fceb61380965b043c41a4..c300f7ce7727eb900b04658201544e65a64c49d4 100644
GIT binary patch
delta 21605
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