Realtime kernel orchestra for native Linux.
khor turns live system activity into deterministic music with eBPF collection, a built-in synth engine, and a control UI for shaping the result in real time.
Live telemetry, system health, preset mapping, and output routing in the built-in dashboard.
|
|
Khor (kernel choir) turns system activity into music on native Linux:
- eBPF collectors aggregate low-overhead system metrics across exec, network, scheduler, block I/O, TCP retransmits, and hardware interrupts
/proc/pressure/memory(PSI) is polled for memory pressure — a slow-moving mood signal- a deterministic music engine maps those signals into notes, rhythm, and synth parameters
- audio plays locally through miniaudio with PulseAudio, PipeWire, or ALSA
- multi-channel MIDI output routes voice roles to separate channels for live DAW integration (melody Ch1, bass Ch2, chords Ch3, percussion Ch10)
- optional outputs mirror the signal to OSC (UDP) and MIDI (ALSA sequencer)
- a built-in web UI exposes status, charts, presets, and live controls
| Collect | Compose | Perform |
|---|---|---|
| Trace kernel activity with eBPF probes and aggregate it into stable realtime metrics. | Convert normalized signals into a deterministic step-sequencer score and synth modulation. | Hear the result locally and steer it live through the dashboard, MIDI, or OSC. |
Linux is the primary target. Root is not the normal run mode; use a one-time
setcapfor eBPF.
See ARCHITECTURE.md for the detailed dataflow.
- eBPF Probes (The Ears): Traces
execve(programs starting),net_dev_queue/netif_receive_skb(network traffic),sched_switch(context switches),block_rq_issue/block_rq_complete(disk I/O),tcp_retransmit_skb(network errors), andirq_handler_entry(hardware interrupts). Memory pressure is read from PSI (/proc/pressure/memory). - Signal Pipeline (The Brain): Normalizes raw event counts into
0.0to1.0control signals using logarithmic scaling and exponential smoothing. Each signal has tuned smoothing — TCP retransmits are kept spiky for percussive triggers, memory pressure is heavily smoothed as a slow mood signal. - Music Engine (The Composer): A step sequencer that deterministically generates music based on these signals. Notes are tagged with MIDI channels per voice role (melody, bass, chords, percussion) for multi-track DAW routing.
- Audio Engine (The Voice): A custom polyphonic synthesizer (subtractive synthesis, ADSR, delay/reverb).
graph TD
subgraph Kernel Space
P_Exec[Trace: Exec]
P_Net[Trace: Network]
P_Sched[Trace: Sched]
P_Block[Trace: Block I/O]
P_TCP[Trace: TCP Retransmit]
P_IRQ[Trace: IRQ]
P_Exec -->|Update| Map[Per-CPU Map Aggregation]
P_Net -->|Update| Map
P_Sched -->|Update| Map
P_Block -->|Update| Map
P_TCP -->|Update| Map
P_IRQ -->|Update| Map
Map -->|Flush Periodically| RB[BPF Ring Buffer]
end
subgraph Userspace Daemon
PSI["PSI Memory Pressure"]
Collector[BPF Collector]
Signal[Signal Normalizer]
Music[Music Engine]
Audio[Audio Engine]
MIDI[MIDI Out]
OSC[OSC Out]
API[HTTP API]
RB -->|Read Events| Collector
Collector -->|Raw Metrics| Signal
PSI -->|Memory Pressure| Signal
Signal -->|Smooth 0..1 Signals| Music
Music -->|Multi-Channel Notes| Audio
Music -->|Multi-Channel Notes| MIDI
Music -->|Notes + Signals| OSC
Audio -->|Sound| Speakers[Speakers]
Signal -->|Metrics| API
Music -->|State| API
end
subgraph UI
Browser[Web Browser]
Browser -->|Poll/SSE| API
end
| Signal | Source | Musical Role |
|---|---|---|
exec |
sys_enter_execve tracepoint |
Note probability, accent chords, filter resonance |
rx |
netif_receive_skb tracepoint |
Reverb mix, melody gating |
tx |
net_dev_queue tracepoint |
Delay mix, arpeggio gating |
csw |
sched_switch tracepoint |
Percussive click probability |
io |
block_rq_complete tracepoint |
Filter cutoff (80Hz–9kHz) |
retx |
tcp_retransmit_skb tracepoint |
Chromatic glitch stabs (deliberately off-scale) |
irq |
irq_handler_entry tracepoint |
Ultra-short hi-hat texture in high octaves |
mem |
/proc/pressure/memory PSI |
Mood — darkens filter, increases reverb, adds resonance strain |
Ubuntu/Debian:
sudo apt-get update
sudo apt-get install -y \
build-essential cmake pkg-config curl \
clang llvm bpftool libbpf-dev linux-headers-$(uname -r) \
libcap2-binFedora:
sudo dnf install -y \
gcc gcc-c++ make cmake pkgconf-pkg-config curl \
clang llvm bpftool libbpf-devel \
libcapOptional (MIDI output via ALSA sequencer):
- Ubuntu/Debian:
sudo apt-get install -y libasound2-dev - Fedora:
sudo dnf install -y alsa-lib-devel
./scripts/linux-check.shYou want /sys/kernel/btf/vmlinux present for CO-RE builds.
./scripts/fetch_deps.sh./scripts/linux-build.shTo serve the UI directly from the daemon in dev:
npm --prefix ui ci
npm --prefix ui run build
./scripts/linux-run.sh --ui-dir ui/distOpen http://127.0.0.1:17321.
Alternative UI dev server (hot reload):
./scripts/linux-run.sh --no-audio --no-bpf
npm --prefix ui run devSet VITE_API_BASE if your daemon is not on the default address/port.
./scripts/install.shEnable eBPF without sudo/root runtime (one-time):
sudo setcap cap_bpf,cap_perfmon,cap_sys_resource,cap_sys_admin,cap_dac_read_search+ep ~/.local/bin/khor-daemon
getcap ~/.local/bin/khor-daemonStart on login:
systemctl --user daemon-reload
systemctl --user enable --now khor.serviceOpen http://127.0.0.1:17321.
Uninstall:
./scripts/uninstall.sh- Do not run the daemon as root if you expect desktop audio. PipeWire/Pulse are per-user services.
- If you must run with elevated privileges for eBPF, prefer the one-time
setcapabove. - Use the UI "Outputs" section to pick a playback device and adjust master gain.
- Debug backends:
- force ALSA:
KHOR_AUDIO_BACKEND=alsa ./scripts/linux-run.sh - force Pulse:
KHOR_AUDIO_BACKEND=pulse ./scripts/linux-run.sh
- force ALSA:
If /api/health shows BPF disabled: EPERM (or similar):
- Verify the binary has caps:
getcap ~/.local/bin/khor-daemon - Ensure your kernel supports
CAP_BPF/CAP_PERFMON(older kernels may require root). - Ubuntu/Debian Users: These distros set
kernel.perf_event_paranoid=3by default, which breaks unprivileged tracepoint attachment even with capabilities. To fix this, temporarily lower the value to the upstream kernel default:To persist this across reboots, add it tosudo sysctl -w kernel.perf_event_paranoid=2
/etc/sysctl.d/60-khor-perf.conf. - For libbpf debug logs:
KHOR_DEBUG_LIBBPF=1 ./scripts/linux-run.sh
Fake mode is off by default. Enable it explicitly:
./scripts/linux-run.sh --fake --no-bpfDefault config path:
${XDG_CONFIG_HOME:-~/.config}/khor/config.json
The UI edits config via PUT /api/config, and the daemon persists it to that file.
Key fields:
listen.host/listen.portui.serve/ui.dirfeatures.bpf/features.audio/features.midi/features.osc/features.fakemusic.*(bpm, key, scale, preset, density, smoothing)audio.*(backend, device, sample_rate, master_gain)midi.*(port, channel)osc.*(host, port)bpf.*(enabled_mask, sample_interval_ms, tgid_allow, tgid_deny, cgroup_id)
~/.local/bin/khor-daemon --helpCommon flags:
--config PATH--listen HOST:PORT--ui-dir PATH--no-bpf,--no-audio--midi,--osc--fake
All APIs are same-origin under /api/*.
GET /api/healthGET /api/metricsGET /api/configPUT /api/config(partial patch supported)GET /api/presetsPOST /api/preset/select?name=ambient|percussive|arp|droneGET /api/audio/devicesPOST /api/audio/device(JSON body:{"device":"id:<hex>"}or{"device":""}for default)POST /api/actions/test_noteGET /api/stream(SSE, ~10Hz)
Examples:
curl -s http://127.0.0.1:17321/api/health | jq .
curl -s http://127.0.0.1:17321/api/metrics | jq .
curl -s http://127.0.0.1:17321/api/presets | jq .
curl -s -X POST 'http://127.0.0.1:17321/api/actions/test_note?midi=62&vel=0.7&dur=0.3'MIDI output uses the ALSA sequencer and requires headers/libs at build time.
On Fedora:
sudo dnf install -y alsa-lib-devel
./scripts/linux-build.sh
./scripts/linux-run.sh --midiThen connect the virtual port to a synth:
aconnect -lNotes are tagged with MIDI channels by voice role so DAWs can route them to separate instruments:
| Channel | Role | Example VST |
|---|---|---|
| 1 | Melody | Pad, lead synth |
| 2 | Bass | Sub bass, 808 |
| 3 | Chords/Pads | Strings, pad |
| 10 | Percussion | Drum kit, glitch FX |
In your DAW, create four tracks each receiving from the "khor" MIDI port on the corresponding channel, assign a different VST instrument to each, and you have live kernel-driven multi-instrument music.
Control signals are also sent as MIDI CC on channel 1:
| CC | Signal |
|---|---|
| 20 | exec (process activity) |
| 21 | rx (network receive) |
| 22 | tx (network transmit) |
| 23 | csw (context switches) |
| 24 | io (block I/O) |
| 74 | filter cutoff |
Enable OSC:
./scripts/linux-run.sh --oscDefault target is 127.0.0.1:9000 (configurable).
Messages:
/khor/note(int channel, int midi, float vel, float dur)/khor/signal(string name, float value01)— names:exec,rx,tx,csw,io,retx,irq,mem/khor/metrics(float exec_s, float rx_kbs, float tx_kbs, float csw_s, float blk_r_kbs, float blk_w_kbs, float retx_s, float irq_s, float mem_pct)
./scripts/linux-build.sh
cd daemon/build
ctest --output-on-failureProject license: see LICENSE.
Third-party notices: see THIRD_PARTY_NOTICES.md.


