From 7b07ff5daeac3faa3c31ceed79c89e3e5d5adc84 Mon Sep 17 00:00:00 2001 From: prompt.ac/@jeffrey Date: Wed, 15 Jul 2026 07:03:17 +0000 Subject: [PATCH] papers/granularity: add live interface + user-comment figures Three captured exhibits (keymaps-style, dense captions), via a reproducible playwright capture script (figures/capture-figures.mjs): - Fig 2: YouTube's 2026 playback-speed control — the coarse itemized menu. - Fig 3: the AC dj piece — a scratchable record on a screen with no record. - Fig 4 (full-width): Spotify's own Idea Exchange — 666 votes for "any speed control for music," status "Not Right Now," marked Case Closed. Demand and the structural refusal in one frame; the caption keeps the reason open (licensing / artist relations / deprioritization — the platform doesn't say). Co-Authored-By: Claude Opus 4.8 (1M context) --- papers/arxiv-granularity/figures/capture-figures.mjs | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ papers/arxiv-granularity/figures/fig-ac-dj.png | 0 papers/arxiv-granularity/figures/fig-spotify-demand.png | 0 papers/arxiv-granularity/figures/fig-youtube-speed.png | 0 papers/arxiv-granularity/granularity.tex | 27 ++++++++++++++++++++++++--- 5 file(s) changed, 110 insertion(s)(+), 3 deletion(s)(-) diff --git a/papers/arxiv-granularity/figures/capture-figures.mjs b/papers/arxiv-granularity/figures/capture-figures.mjs new file mode 100644 --- /dev/null +++ b/papers/arxiv-granularity/figures/capture-figures.mjs @@ -0,0 +1,86 @@ +// capture-figures.mjs — live browser screenshots for the paper's exhibits. +// Uses the repo's playwright + installed Google Chrome (channel: "chrome"). +// node papers/arxiv-granularity/figures/capture-figures.mjs +// Writes PNGs into this figures/ dir. Each capture is isolated so one +// failure does not sink the others. Cookie banners are declined/dismissed. +import { chromium } from "playwright"; + +const DIR = new URL(".", import.meta.url).pathname; +const browser = await chromium.launch({ channel: "chrome", headless: true }); + +async function shot(name, fn) { + const ctx = await browser.newContext({ + viewport: { width: 1280, height: 820 }, + deviceScaleFactor: 2, + userAgent: + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36", + }); + const page = await ctx.newPage(); + try { + await fn(page); + console.log("OK " + name); + } catch (e) { + console.log("FAIL " + name + " :: " + String(e.message).slice(0, 160)); + } finally { + await ctx.close(); + } +} + +const dismiss = async (page, selectors) => { + for (const s of selectors) { + try { + const el = await page.waitForSelector(s, { timeout: 2500 }); + if (el) { await el.click(); await page.waitForTimeout(600); return true; } + } catch {} + } + return false; +}; + +// 1) AC dj piece — our own turntable/scratch player. +await shot("fig-ac-dj", async (page) => { + await page.goto("https://aesthetic.computer/dj", { waitUntil: "load", timeout: 60000 }); + await page.waitForTimeout(9000); // let the piece boot and draw the platter + await page.screenshot({ path: DIR + "fig-ac-dj.png" }); +}); + +// 2) YouTube playback-speed menu — the coarse itemized control. +await shot("fig-youtube-speed", async (page) => { + await page.goto("https://www.youtube.com/watch?v=jNQXAC9IVRw", { + waitUntil: "domcontentloaded", + timeout: 60000, + }); + await dismiss(page, [ + 'button[aria-label*="Reject"]', + 'button:has-text("Reject all")', + 'button:has-text("Accept all")', + 'tp-yt-paper-button:has-text("Accept all")', + ]); + await page.waitForSelector(".ytp-settings-button", { timeout: 30000 }); + await page.hover(".html5-video-player").catch(() => {}); + await page.click(".ytp-settings-button"); + await page.waitForTimeout(500); + await page.click('.ytp-menuitem:has-text("Playback speed")'); + await page.waitForTimeout(800); + const menu = await page.$(".ytp-popup.ytp-settings-menu"); + if (menu) await menu.screenshot({ path: DIR + "fig-youtube-speed.png" }); + else await page.screenshot({ path: DIR + "fig-youtube-speed.png" }); +}); + +// 3) Spotify community — a real "add speed control for music" idea thread. +await shot("fig-spotify-demand", async (page) => { + await page.goto( + "https://community.spotify.com/t5/Live-Ideas/All-Platforms-Change-playback-speed-when-listening-to-music/idi-p/4794106", + { waitUntil: "domcontentloaded", timeout: 60000 }, + ); + await dismiss(page, [ + "#onetrust-reject-all-handler", + "#onetrust-accept-btn-handler", + 'button:has-text("Reject")', + 'button:has-text("Accept")', + ]); + await page.waitForTimeout(1500); + await page.screenshot({ path: DIR + "fig-spotify-demand.png" }); +}); + +await browser.close(); +console.log("done"); diff --git a/papers/arxiv-granularity/figures/fig-ac-dj.png b/papers/arxiv-granularity/figures/fig-ac-dj.png new file mode 100644 --- /dev/null +++ b/papers/arxiv-granularity/figures/fig-ac-dj.png diff --git a/papers/arxiv-granularity/figures/fig-spotify-demand.png b/papers/arxiv-granularity/figures/fig-spotify-demand.png new file mode 100644 --- /dev/null +++ b/papers/arxiv-granularity/figures/fig-spotify-demand.png diff --git a/papers/arxiv-granularity/figures/fig-youtube-speed.png b/papers/arxiv-granularity/figures/fig-youtube-speed.png new file mode 100644 --- /dev/null +++ b/papers/arxiv-granularity/figures/fig-youtube-speed.png diff --git a/papers/arxiv-granularity/granularity.tex b/papers/arxiv-granularity/granularity.tex --- a/papers/arxiv-granularity/granularity.tex +++ b/papers/arxiv-granularity/granularity.tex @@ -195,7 +195,14 @@ \section{The Coarse Menu, Itemized} It is worth being specific about how little the dominant platforms actually give you, because the poverty is easy to miss when you have never been offered anything better. -\textbf{YouTube} offers discrete presets from 0.25$\times$ to 2$\times$, and, more recently, a "custom" slider that moves in 0.05 steps within that range~\citep{youtube2026speed}. This is the generous end of the consumer spectrum, and it is still a global multiplier with no gestural surface, no pitch control, and no coupling to the content: you set a number and the whole video obeys it. Scrubbing the timeline is separate from setting the speed, and neither one is expressive --- the scrub bar is a seek, not a performance. +\textbf{YouTube} offers discrete presets from 0.25$\times$ to 2$\times$, and, more recently, a "custom" slider that moves in 0.05 steps within that range (Figure~\ref{fig:youtube})~\citep{youtube2026speed}. This is the generous end of the consumer spectrum, and it is still a global multiplier with no gestural surface, no pitch control, and no coupling to the content: you set a number and the whole video obeys it. Scrubbing the timeline is separate from setting the speed, and neither one is expressive --- the scrub bar is a seek, not a performance. + +\begin{figure}[t] +\centering +\includegraphics[width=\columnwidth]{figures/fig-youtube-speed} +\caption{YouTube's playback-speed control, 2026 --- the generous end of the consumer spectrum. A slider that snaps to a row of presets ($1{\times}$ ``Normal'', $1.25$, $1.5$, $2$, and a Premium-gated $3.0$), applied globally to the whole video. There is no coupling to the content, no pitch to bend, and nothing continuous under the finger. The ``instrument'' is a list of numbers you pick from and release.} +\label{fig:youtube} +\end{figure} \textbf{Spotify} is the instructive case. For music --- the core of the product --- there is no speed control at all. Every track plays at 1.0$\times$, always, and years of it sitting among the most-requested items in the community forums have not changed it~\citep{spotify2026speed}. Speed adjustment exists only for podcasts and audiobooks, as a small set of increments from 0.5$\times$ to 3$\times$. The company that convinced the world to rent its music instead of owning it has decided that the one thing you may not do to a song is change how fast it moves. @@ -230,7 +237,14 @@ Two details make it feel like an instrument rather than a control. First, it has \emph{inertia}. Let go while moving and the tape keeps spinning, the speed decaying by a fixed factor each frame until it coasts to a stop --- a flywheel, so you can flick through footage and let it settle. Second, the \emph{audio bends with it}. Scrubbing does not mute the tape and jump silently between frames; it pitch-shifts the sound in real time as the speed changes, the way a reel does when you drag it under the head. That pitch bend is not faked --- underneath sits a real granular time-stretch engine (\texttt{lib/speaker-bundled.mjs}) that keeps pitch and duration as separate quantities and resynthesizes grains at whatever rate the scrub demands. The same pitch-preserving-or-bending stretch that studios license as \emph{elastic audio} (\S4) is running in the browser, driven by your finger, as the default way to watch back a tape. On screen you get a playhead and a live speed readout with a direction arrow --- but you are not reading the number to set the speed. The number is just confirming what your hand already did. \subsection{The dj piece is a turntable} -\ac{} also ships an actual record. The \texttt{dj} piece (\texttt{disks/dj.mjs}) draws a platter spinning at 33\,1/3\,rpm with a needle on it, and you scratch it. The gesture is radial: the interface takes the angular change of your pointer around the center of the platter and turns it into a signed rate --- twist the record forward and it plays forward with the pitch riding up, wind it back and it reverses. There is a pitch fader, and a waveform strip you can needle-drop into. This is the turntablist's mapping from \S4 --- position to phase, velocity to rate and pitch --- rebuilt as a piece you can open by typing a word. +\ac{} also ships an actual record (Figure~\ref{fig:acdj}). The \texttt{dj} piece (\texttt{disks/dj.mjs}) draws a platter spinning at 33\,1/3\,rpm with a needle on it, and you scratch it. + +\begin{figure}[t] +\centering +\includegraphics[width=\columnwidth]{figures/fig-ac-dj} +\caption{The \ac{} \texttt{dj} piece --- a scratchable record on a screen that has no record on it. The grooved platter turns under a tonearm and needle; dragging it radially bends pitch and rate in real time, and the label waits for a track (\texttt{drop an audio file / or: dj:https://url.mp3}). The transport strip at the bottom ($-$, \textsc{play}, $+$, $1{\times}$) is the fallback --- but it is the record, not the readout, that you play.} +\label{fig:acdj} +\end{figure} The gesture is radial: the interface takes the angular change of your pointer around the center of the platter and turns it into a signed rate --- twist the record forward and it plays forward with the pitch riding up, wind it back and it reverses. There is a pitch fader, and a waveform strip you can needle-drop into. This is the turntablist's mapping from \S4 --- position to phase, velocity to rate and pitch --- rebuilt as a piece you can open by typing a word. I will be honest about the seam, because the two versions differ. In the browser, the audio backend cannot truly run in reverse, so forward drags are audibly pitch-bent while backward drags scrub by seeking silently --- vinyl-ish, within the limits of a web audio element, and the code says so in its own comments. The \ac{} native build has no such limit: it scratches a bespoke C audio deck that runs genuinely bidirectionally, clamped to $\pm4\times$, mixing a fractional ring-buffer read with interpolation so reverse playback is real sound, not a silent seek. So the web \texttt{dj} is an honest approximation and the native one is the real thing --- but both put a scratchable record on a screen that has no record on it. @@ -250,7 +264,14 @@ Here is the false version: \emph{nobody offers this}. They do. HCI research shipped continuous, gestural, bidirectional audio scrubbing --- pitch-preserving stretch, the sound as its own feedback --- two decades ago: Lee and Borchers' \emph{DiMaß} did in 2006 almost exactly what \S5's tape player does now~\citep{lee2006dimass}, and Beaudouin-Lafon had already named the underlying move, \emph{instrumental interaction} --- the interface as an instrument acting on the object, not a proxy describing changes to it~\citep{beaudouinlafon2000instrumental}. Consumers have had the instrument too. \emph{djay} puts a scratchable turntable over Apple Music, Spotify, and TIDAL and has passed seventy million downloads~\citep{algoriddim2024djay}; a whole mobile-DJ category sits beside it; \emph{Anytune} serves the continuous, pitch-decoupled, fine-tempo listening niche as a practice tool~\citep{anytune2024}. A dominant platform has even patented the metaphor: Google's gesture-driven video scrubbing is described, in the patent itself, as ``a disc jockey scratching records''~\citep{google2019scrubbingvideo}. The instrument is neither new nor unbuilt. So here is the claim that actually survives, and it is sharper than the one it replaces. The instrument exists everywhere except the one place most listening and watching happens: the default transport bar of the surfaces you passively consume through. It is always walled off into a \emph{separate} app --- a DJ tool, a practice tool --- that you leave your feed to open, never offered as the way you touch the recording you came to hear. And when someone tried to bring it inside, the incumbent shut the door: \emph{Pacemaker} shipped native, Spotify-integrated mixing to a mainstream audience, and Spotify revoked the DJ API that made it possible~\citep{pacemaker2018}. That is not a market being served. It is the exact structural refusal this paper is about, caught in the act. -That refusal is the shape of the opening. The enabling technology --- pitch-preserving stretch, low-latency audio, continuous multi-touch input --- is a commodity available to anyone. The demand is visibly unmet even in its crudest form: for years, one of the most-requested items on Spotify's own forums has been \emph{any} speed control for music, and the answer has stayed no~\citep{spotify2026speed}. People are asking for a worse version of the thing this paper describes and cannot get even that. The first \emph{default transport} to treat playback as an instrument --- continuous, gestural, felt, with the media as its own feedback --- would not be competing on a feature. It would be offering a different relationship to recorded sound and image than the incumbents, locked by catalog licensing and a decade of frozen UI, have been willing to build. +That refusal is the shape of the opening. The enabling technology --- pitch-preserving stretch, low-latency audio, continuous multi-touch input --- is a commodity available to anyone. The demand is visibly unmet even in its crudest form: for years, one of the most-requested items on Spotify's own forums has been \emph{any} speed control for music, and the answer has stayed no (Figure~\ref{fig:spotify})~\citep{spotify2026speed}. + +\begin{figure*}[t] +\centering +\includegraphics[width=0.88\textwidth]{figures/fig-spotify-demand} +\caption{Demand and refusal in one frame, from Spotify's own Idea Exchange. The request is only for the \emph{crude} version --- \emph{any} speed control for music --- and it has 666 votes, citing an older thread with 573. Its status is ``Not Right Now,'' and Spotify's response marks the idea \emph{Case Closed} because that status ``hasn't changed in over 3 years.'' Whatever the actual reason --- catalog licensing, artist relations, or plain deprioritization; the platform does not say --- the answer is not ``impossible.'' It is ``no.'' This is the structural refusal the paper is about, in the incumbent's own words.} +\label{fig:spotify} +\end{figure*} People are asking for a worse version of the thing this paper describes and cannot get even that. The first \emph{default transport} to treat playback as an instrument --- continuous, gestural, felt, with the media as its own feedback --- would not be competing on a feature. It would be offering a different relationship to recorded sound and image than the incumbents, locked by catalog licensing and a decade of frozen UI, have been willing to build. \section{Limitations} -- tangled.sh