Lo-fi beats while Claude works: a soft loop starts on long turns and stops the moment it's done. /lofi toggles.

Lo-fi beats while Claude works. When a turn runs longer than a few seconds, a soft, seamless loop starts (Rhodes chords, warm bass, swung drums, vinyl crackle). It stops the moment Claude finishes, and the status line shows what's playing ("♪ beats to refactor to").
The loop is original, synthesized for this mod, so there are no licensing worries.
Requires Claude Code 2.1.293 or later: mods are an early-access feature and gain events with each release. Check with claude --version; update with claude update.
/plugin marketplace add lakmadev/claude-mods
/plugin install lofi-mode@claude-mods
/lofi toggles it (/lofi on, /lofi off); the choice is remembered. Settings (/config): volume (0.35), delaySeconds (4). Audio plays on macOS.
hooks/register.ts 61 lines1import type { EngineInterface, Register, Timer } from 'claude-code'
2
3const NOW_PLAYING = ['♪ lo-fi beats to code to', '♪ chillhop for compilers', '♪ beats to refactor to', '♪ rainy-day debugging mix']
4
5let isEnabled = true
6let pending: Timer | undefined
7let playing: AbortController | undefined
8
9// Volume 0..1 maps onto the engine's gain 0..4, kept gentle.
10export const gainFor = (volume: number) => Math.min(1, Math.max(0, volume)) * 1.5
11
12async function start($: EngineInterface, gain: number) {
13 if (playing || !isEnabled) return
14 playing = new AbortController()
15 $.ui.status(NOW_PLAYING[Math.floor(Math.random() * NOW_PLAYING.length)])
16 await $.audio.play({ asset: 'assets/lofi.wav' }, { shouldLoop: true, gain, signal: playing.signal }).catch(() => undefined)
17}
18
19function stop($: EngineInterface) {
20 pending?.cancel()
21 pending = undefined
22 playing?.abort()
23 playing = undefined
24 $.ui.status(undefined)
25}
26
27export const register: Register = (on, options) => {
28 const gain = gainFor(Number(options.volume ?? 0.35))
29 const delayMs = Number(options.delaySeconds ?? 4) * 1000
30
31 on('session.start', async ($, e, next) => {
32 await $.command.register({ name: 'lofi', description: 'Toggle lo-fi beats while Claude works', argumentHint: '[on|off]' })
33 isEnabled = ((await $.store.get('enabled')) as boolean | undefined) ?? true
34 return next(e)
35 })
36
37 on('turn.start', async ($, e, next) => {
38 if (isEnabled && !playing && !pending) pending = $.clock.after(delayMs, () => void start($, gain))
39 return next(e)
40 })
41
42 on('turn.complete', async ($, e, next) => {
43 const done = await next(e)
44 if (e.agentId === undefined) stop($)
45 return done
46 })
47
48 on('session.end', async ($, e, next) => {
49 stop($)
50 return next(e)
51 })
52
53 on('command.run', { command: 'lofi' }, async ($, e) => {
54 const arg = e.args.trim()
55 isEnabled = arg === 'on' ? true : arg === 'off' ? false : !isEnabled
56 await $.store.set('enabled', isEnabled)
57 if (!isEnabled) stop($)
58 return { text: isEnabled ? '♪ lo-fi mode on: beats start when Claude works for a while.' : 'lo-fi mode off.' }
59 })
60}
61