Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: disable wifi init (notepat scans every 2s), mouse-interactive DJ scratch

- WiFi fully disabled — notepat.mjs calls wifi.scan every 120 frames
causing 65ms+ frame drops from iw scan. Will re-enable once scan
is non-blocking.
- DJ turntable scratch is now mouse/touchpad interactive:
- Touch/click platter to grab it, drag to scratch
- Drag down = forward, up = rewind (100px = 2s seek)
- Bigger platters with groove rings and spinning needle
- Visual feedback: platter highlights, needle follows position

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+88 -18
+85 -15
fedac/native/pieces/dj.mjs
··· 13 13 let activeDeck = 0; // 0 = A, 1 = B 14 14 let scratching = [false, false]; // per-deck scratch mode 15 15 let scratchPos = [0, 0]; // scratch position (virtual "angle") 16 + let dragging = -1; // which deck is being mouse-dragged (-1=none) 17 + let dragStartY = 0; // mouse Y at drag start 18 + let dragStartPos = 0; // deck position at drag start 16 19 let crossfader = 0.5; 17 20 let mounted = false; 18 21 let message = ""; ··· 132 135 } 133 136 } 134 137 135 - function act({ event: e, sound, system, tts }) { 136 - if (!e.is("keyboard:down")) return; 138 + // Platter geometry (computed in paint, used in act) 139 + let platterA = { cx: 0, cy: 0, r: 0 }; 140 + let platterB = { cx: 0, cy: 0, r: 0 }; 141 + 142 + function act({ event: e, sound, system, tts, screen }) { 137 143 const dk = sound?.deck; 138 144 const decks = dk?.decks || [{}, {}]; 145 + const w = screen?.width || 320; 146 + const h = screen?.height || 240; 147 + const P = 4; 148 + const deckW = Math.floor((w - P * 3) / 2); 149 + 150 + // --- Mouse scratch interaction --- 151 + if (e.is("touch")) { 152 + const mx = e.x, my = e.y; 153 + // Check if touching a platter 154 + for (let i = 0; i < 2; i++) { 155 + const pl = i === 0 ? platterA : platterB; 156 + const dx = mx - pl.cx, dy = my - pl.cy; 157 + if (dx * dx + dy * dy <= pl.r * pl.r) { 158 + dragging = i; 159 + dragStartY = my; 160 + dragStartPos = decks[i]?.position || 0; 161 + if (decks[i]?.playing) dk?.pause(i); 162 + return; 163 + } 164 + } 165 + return; 166 + } 167 + 168 + if (e.is("draw") && dragging >= 0) { 169 + const d = decks[dragging]; 170 + if (d?.loaded) { 171 + const dy = e.y - dragStartY; 172 + // Drag down = forward, drag up = rewind. 100px = 2 seconds 173 + const seekTo = Math.max(0, Math.min(d.duration, dragStartPos + dy * 0.02)); 174 + dk?.seek(dragging, seekTo); 175 + scratchPos[dragging] += (e.y - dragStartY) * 0.5; 176 + dragStartY = e.y; 177 + dragStartPos = seekTo; 178 + } 179 + return; 180 + } 181 + 182 + if (e.is("lift") && dragging >= 0) { 183 + dragging = -1; 184 + return; 185 + } 186 + 187 + if (!e.is("keyboard:down")) return; 139 188 140 189 // --- Global --- 141 190 if (e.is("keyboard:down:escape")) { system?.jump?.("prompt"); return; } ··· 351 400 return; 352 401 } 353 402 354 - // Turntable platter (circle) 355 - const cx = x0 + deckW - 30; 356 - const cy = P + 30; 357 - const r = 20; 358 - ink(25, 25, 40); 403 + // Turntable platter (large interactive circle) 404 + const r = Math.min(Math.floor(deckW * 0.3), Math.floor(deckH * 0.35)); 405 + const cx = x0 + deckW - r - 6; 406 + const cy = P + r + 6; 407 + // Store for mouse hit testing 408 + if (idx === 0) { platterA.cx = cx; platterA.cy = cy; platterA.r = r; } 409 + else { platterB.cx = cx; platterB.cy = cy; platterB.r = r; } 410 + const isDragging = dragging === idx; 411 + // Platter background 412 + ink(isDragging ? 35 : 20, isDragging ? 35 : 20, isDragging ? 50 : 35); 359 413 circle(cx, cy, r, true); 360 - ink(isActive ? 60 : 35, isActive ? 60 : 35, isActive ? 80 : 50); 414 + // Groove rings 415 + for (let gr = Math.floor(r * 0.4); gr < r; gr += 3) { 416 + ink(isDragging ? 45 : 28, isDragging ? 45 : 28, isDragging ? 60 : 42); 417 + circle(cx, cy, gr, false); 418 + } 419 + // Rim 420 + ink(isActive ? 80 : 45, isActive ? 80 : 45, isActive ? 100 : 60); 361 421 circle(cx, cy, r, false); 422 + // Label dot (center) 423 + ink(isDragging ? 255 : 60, isDragging ? 100 : 60, isDragging ? 60 : 80); 424 + circle(cx, cy, Math.max(3, Math.floor(r * 0.15)), true); 362 425 363 - // Spinning indicator (rotates with position) 364 - const angle = (d.position || 0) * 3; 426 + // Spinning needle (rotates with position + scratch) 427 + const angle = ((d.position || 0) + scratchPos[idx] * 0.01) * 3; 365 428 const nx = cx + Math.cos(angle) * (r - 4); 366 429 const ny = cy + Math.sin(angle) * (r - 4); 367 430 ink(d.playing ? 100 : 60, d.playing ? 255 : 120, d.playing ? 100 : 60); 368 - circle(Math.floor(nx), Math.floor(ny), 2, true); 431 + circle(Math.floor(nx), Math.floor(ny), Math.max(2, Math.floor(r * 0.08)), true); 432 + // Line from center to needle 433 + ink(50, 50, 70); 434 + line(cx, cy, Math.floor(nx), Math.floor(ny)); 369 435 370 - // Scratch indicator 371 - if (scratching[idx]) { 372 - ink(255, 80, 80); 373 - write("SCR", { x: cx - 9, y: cy + r + 3, size: 1, font: F }); 436 + // Drag hint 437 + if (isActive && !isDragging) { 438 + ink(dim, dim, dim + 10); 439 + write("drag", { x: cx - 12, y: cy + r + 3, size: 1, font: F }); 440 + } 441 + if (isDragging) { 442 + ink(255, 100, 60); 443 + write("SCRATCH", { x: cx - 20, y: cy + r + 3, size: 1, font: F }); 374 444 } 375 445 376 446 // Title
+3 -3
fedac/native/src/ac-native.c
··· 2286 2286 if (!wifi_disabled) { 2287 2287 if (!headless && display) 2288 2288 draw_boot_status(&graph, screen, display, "starting wifi...", pixel_scale); 2289 - wifi = wifi_init(); 2290 - // Don't autoconnect — iw scan blocks DRM page flip for 65ms+. 2291 - // WiFi scans only when user explicitly requests from prompt. 2289 + // WiFi disabled — notepat.mjs scans every 2s which causes 65ms+ 2290 + // frame drops from iw scan. Re-enable once scan is non-blocking. 2291 + wifi = NULL; 2292 2292 } else { 2293 2293 if (!headless && display) 2294 2294 draw_boot_status(&graph, screen, display, "wifi disabled", pixel_scale);