The first cut of ?pi=1 switched off the two things that look most expensive - the backdrop-filter glass blur and the decorative CSS animation loops - and made the device slower, not faster: 53.5% of a core idle became 118%. Measured on musicdolphin, sum of the Firefox process tree, idle on the browse screen, 15s average: full app 53.5% + ambient canvas at half resolution 25.0% <- the whole win + 30fps cap on top of that 25.0% (no idle change) + decorative CSS animation loops off 24.6% (noise; left on) + backdrop-filter glass blur off 118.1% <- 5x worse The blur is what promotes each glass panel to its own compositing layer. Without it the animated canvas and the whole album grid above it collapse into one layer and every canvas frame repaints all of it. The most expensive-looking CSS in the app is what was keeping the rest of it cheap. SHOW_GLASS_BLUR and SHOW_DECORATIVE_ANIMATIONS therefore go back to unconditionally on, in both the music app and the typing game, with the numbers written down next to them so the next person does not repeat this. What is left is one real change - paint a quarter of the pixels - plus three caps that only bite while something is playing and so are not in the table above: 30fps on the canvas, 60 bubbles alive at once, and ten progress-bar re-renders a second. Those three are unmeasured; measuring them means playing audio. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
131 lines
5.9 KiB
TypeScript
131 lines
5.9 KiB
TypeScript
/** Per-group colors and identity, and the feature toggles that have gone back and
|
|
* forth while this design was under review - flip one here rather than hunting
|
|
* through the components that read it. Most toggles are about the browse screen's
|
|
* shelf/row look, but some (`SHOW_GLASS_BLUR`, `SHOW_AMBIENCE`,
|
|
* `SHOW_DECORATIVE_ANIMATIONS`) reach further: the play view, the player bar, the
|
|
* room page and `App.tsx` all read this module too, wherever they share the same
|
|
* "cut this on weak hardware" knob.
|
|
*
|
|
* `SHOW_ROW_TITLES` and `SHOW_ROW_ICONS` both `true` reproduces the original shelf
|
|
* header - an icon badge plus label button above each row's tiles. */
|
|
|
|
import type { CSSProperties } from "react";
|
|
|
|
import type { Group } from "./search";
|
|
import type { UiState } from "./keyboard";
|
|
|
|
// ---------------------------------------------------------------- toggles --
|
|
|
|
/** Tint each shelf panel and its rows with the group's hue below. */
|
|
export const SHOW_ROW_TINT = false;
|
|
|
|
/** Show each shelf's title text ("Musik", "Hörbücher", "Podcasts") above its tiles. */
|
|
export const SHOW_ROW_TITLES = true;
|
|
|
|
/** Show the small icon badge (🎵/📖/🎙️) beside the title. Stands alone fine with
|
|
* `SHOW_ROW_TITLES` off (an icon-only header), but the original look needs both. */
|
|
export const SHOW_ROW_ICONS = true;
|
|
|
|
/** Vertical gap between shelves on the root browse screen, in px. */
|
|
export const ROW_SPACING = 25;
|
|
|
|
/** Fade the album/category grid in (with a slight upward slide) when a group or
|
|
* category is entered, and the album modal in when it opens. One animation per
|
|
* container, not per card, so cost stays flat regardless of grid size. */
|
|
export const ANIMATE_VIEW_TRANSITIONS = true;
|
|
|
|
/** Frost the glass panels/cards/rows with a real backdrop blur.
|
|
*
|
|
* The obvious thing to cut on weak hardware, and on the Pi that is exactly wrong: it
|
|
* measured five times *worse* with the blur off (118% of a core against 25%). The blur
|
|
* is what promotes each panel to its own compositing layer; without it the animated
|
|
* canvas and the whole album grid above it share one layer and every canvas frame
|
|
* repaints all of it. See the table in lib/lowPower.ts before touching this. */
|
|
export const SHOW_GLASS_BLUR = true;
|
|
|
|
/** Render the play view's animated canvas background (gradient + bubbles, both
|
|
* redrawn every frame at 60fps). Off falls back to `.stage`'s own static CSS
|
|
* gradient, which is still underneath the canvas either way.
|
|
*
|
|
* Deliberately still on under `?pi=1`: it is the app's whole look, and
|
|
* `AMBIENCE_QUALITY` in lib/lowPower.ts makes it affordable (quarter of the pixels,
|
|
* half the frames) rather than making it go away. */
|
|
export const SHOW_AMBIENCE = true;
|
|
|
|
/** Run the purely decorative CSS animation loops: the room page's bubble field and
|
|
* the dolphin mascot's bob/swim. Left on even under `?pi=1`: switching them off on the
|
|
* Pi moved the idle figure by 0.4 percentage points, which is noise, and they are the
|
|
* kind of detail this app is for. */
|
|
export const SHOW_DECORATIVE_ANIMATIONS = true;
|
|
|
|
// ------------------------------------------------------------------ colors --
|
|
|
|
/** Per-group hue (oklch degrees). The play view's ambient water uses the same
|
|
* family (`ambienceBaseFor` in `lib/ambience.ts`), so a tinted shelf already hints
|
|
* at the mood its albums play into - and so does the book/music card tint in
|
|
* `lib/covers.ts`, which reads `GROUP_HUE.audiobooks`/`GROUP_HUE.music` too. */
|
|
export const GROUP_HUE: Record<Group, number> = {
|
|
music: 210, // today's sea - Dolphin Beats' baseline hue
|
|
audiobooks: 55, // warm amber - richer than the old shared hue (80)'s dull brown
|
|
podcasts: 300, // the one otherwise-unused family
|
|
};
|
|
|
|
export const GROUP_ICON: Record<Group, string> = {
|
|
music: "🎵",
|
|
audiobooks: "📖",
|
|
podcasts: "🎙️",
|
|
};
|
|
|
|
export const GROUP_LABEL: Record<Group, string> = {
|
|
music: "Musik",
|
|
audiobooks: "Hörbücher",
|
|
podcasts: "Podcasts",
|
|
};
|
|
|
|
/** How much horizontal space the tab rail's own glass bar claims on the right edge
|
|
* (its `right` inset plus its rendered width) - scrollable content reserves this much
|
|
* padding on that side so a full row/grid never renders underneath it. Keep in sync
|
|
* with the geometry in `TabRail.tsx`. */
|
|
export const TAB_RAIL_CLEARANCE = 88;
|
|
|
|
/** Icon/label for the vertical tab rail - one entry per `UiState["page"]`. The
|
|
* smarthome tab only renders when Home Assistant is configured (see `App.tsx`); the
|
|
* other two always show. */
|
|
export const PAGE_ICON: Record<UiState["page"], string> = {
|
|
// A plain Unicode symbol, not the 🎵 emoji: that glyph's own built-in colour is a
|
|
// muted grey-blue on at least one real platform, which is illegible against the tab
|
|
// rail's own glass background and does not respond to `color` the way text does.
|
|
music: "♪",
|
|
room: "💡",
|
|
typing: "⌨️",
|
|
};
|
|
|
|
export const PAGE_LABEL: Record<UiState["page"], string> = {
|
|
music: "Musik",
|
|
room: "Mein Zimmer",
|
|
typing: "Tippen",
|
|
};
|
|
|
|
/** A shelf/row's frosted background and border, tinted with its group's hue - or,
|
|
* with `SHOW_ROW_TINT` off, `{}` so `.glass-panel`'s own neutral CSS shows through. */
|
|
export function glassTint(hue: number): CSSProperties {
|
|
if (!SHOW_ROW_TINT) return {};
|
|
return {
|
|
background: `linear-gradient(160deg, oklch(55% 0.1 ${hue} / .32), oklch(30% 0.06 ${hue} / .14))`,
|
|
borderColor: `oklch(75% 0.09 ${hue} / .4)`,
|
|
};
|
|
}
|
|
|
|
/** One list row's background, tinted with its group's hue and brighter/more opaque
|
|
* while it's the currently-playing row - or, with `SHOW_ROW_TINT` off, the same
|
|
* neutral highlight the row used before tinting existed. */
|
|
export function rowTint(hue: number, highlighted: boolean): CSSProperties {
|
|
if (!SHOW_ROW_TINT) {
|
|
return { background: `oklch(97% 0.01 210 / ${highlighted ? ".22" : ".10"})` };
|
|
}
|
|
return {
|
|
background: `oklch(${highlighted ? "62% 0.12" : "42% 0.08"} ${hue} / ${highlighted ? ".34" : ".2"})`,
|
|
borderColor: `oklch(78% 0.09 ${hue} / ${highlighted ? ".5" : ".28"})`,
|
|
};
|
|
}
|