A simple XY-oscilloscope written in Go.
  • Go 90.8%
  • Roff 6.4%
  • Nix 2.6%
  • Shell 0.2%
Find a file
2026-07-17 20:37:59 +02:00
align fix: align add period refinement 2026-07-17 20:37:59 +02:00
audio feat: refactor config (sorry but this is way cleaner) 2026-01-10 23:59:21 +01:00
bars feat: add waterfall mode 2026-03-05 21:36:57 +01:00
beatdetect feat: refactor config (sorry but this is way cleaner) 2026-01-10 23:59:21 +01:00
config fix: align add period refinement 2026-07-17 20:37:59 +02:00
fastsqrt fix: add extra fast sqrt stuff 2024-10-21 19:39:34 +02:00
filter feat: huge shader overhaul 2025-12-31 18:29:35 +01:00
fonts feat: refactor config (sorry but this is way cleaner) 2026-01-10 23:59:21 +01:00
icons feat: huge shader overhaul 2025-12-31 18:29:35 +01:00
interpolate feat: refactor config (sorry but this is way cleaner) 2026-01-10 23:59:21 +01:00
kaiser feat: refactor config (sorry but this is way cleaner) 2026-01-10 23:59:21 +01:00
media feat: huge shader overhaul 2025-12-31 18:29:35 +01:00
particles feat: add particles 2024-11-03 14:48:53 +01:00
shaders feat: add waterfall mode 2026-03-05 21:36:57 +01:00
slew feat: add multiple traces 2026-04-23 21:06:00 +02:00
splash feat: refactor config (sorry but this is way cleaner) 2026-01-10 23:59:21 +01:00
utils fix: fix align 2026-07-17 19:55:02 +02:00
vendor feat: add waterfall mode 2026-03-05 21:36:57 +01:00
vu feat: refactor config (sorry but this is way cleaner) 2026-01-10 23:59:21 +01:00
waterfall feat: add waterfall mode 2026-03-05 21:36:57 +01:00
.gitignore feat: add windows cross compilation capabilities to nix shell 2025-08-30 18:17:58 +02:00
build.sh init: init commit 2024-10-20 22:58:09 +02:00
flake.lock feat: bump flake.lock 2026-06-11 19:03:15 +02:00
flake.nix feat: add darwin support 2026-06-25 20:00:21 +02:00
go.mod feat: add waterfall mode 2026-03-05 21:36:57 +01:00
go.sum feat: add waterfall mode 2026-03-05 21:36:57 +01:00
main.go feat: add multiple traces 2026-04-23 21:06:00 +02:00
out.png feat: rework ring buffer and add auto bpm detection and vis 2025-07-15 19:56:02 +02:00
README.md fix: align add period refinement 2026-07-17 20:37:59 +02:00
wslbuild.sh add: windows build script 2024-10-21 13:57:06 +02:00
xyosc.1 ver: tag 0.0.2 with man page added 2026-03-15 11:24:19 +01:00
xyosc.desktop xyosc: create desktop entry 2025-01-03 13:05:51 +01:00

xyosc

A simple XY-oscilloscope written in Go.

image image

image image

Instalation

Arch

Install ontake-xyosc-git

NixOS

Use this repo as a flake. You can test xyosc out with nix run github:make-42/xyosc for example

Or, install permanantly with flakes:

Minimal flake.nix

{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    xyosc.url = "github:make-42/xyosc";
  };
  # ...

  outputs = inputs @ {
    nixpkgs,
    xyosc,
    ...
  }: let
    system = "x86_64-linux"; # change to whatever your system should be
  in {
    nixosConfigurations."${host}" = nixpkgs.lib.nixosSystem {
      specialArgs = {
        inherit system;
        inherit inputs;
      };
    };
  };
}

And to install

{  inputs, pkgs, ... }:
environment.systemPackages = [
    inputs.xyosc.packages.${pkgs.system}.default
];

And the xyosc binary should be available

Configuration

The configuration file can be found at ~/.config/ontake/xyosc/config.yml

Note: xyosc might not chose the right device to get audio from by default. When you run xyosc it displays a list of capture devices with indices, change the capturedeviceindex option to the right index if it isn't the right one by default.

You should also always set read buffers smaller than the ring buffer by a few frames' worth of samples to prevent race conditions.

Features

  • XY mode, single channel mode (L/R/Mix modes), bars - can be toggled with the F key
  • particles
  • MPRIS support
  • frequency separation
  • theming support
  • shader support (using ebitengine's Kage shader language) - you can cycle through presets with the P key
  • (soon: waterfall mode + keyboard vis (with fading anims))

NixOS

Here is an example of how to configure xyosc using Nix.

{ pkgs, lib, ... }:

{
  xdg.configFile.xyosc = {
    enable = true;
    target = "ontake/xyosc/config.yml";
    text = pkgs.lib.generators.toYAML { } {
      fpscounter = false;
      showfilterinfo = true;
      showmpris = false;
      mpristextopacity = 255;
      targetfps = 120;
      accentcolor = "#FF0000";
      # rest of config below
    };
  };
}

Typical configuration file

app:
  targetfps: 240
  fpscounter: false
  defaultmode: 0
  splash:
    enable: true
    staticsecs: 1
    transsecs: 1
window:
  width: 1000
  height: 1000
  resizable: false
fonts:
  usesystemfont: true
  systemfont: Maple Mono NF
audio:
  capturedevicematchindex: 2
  capturedevicematchname: "Monitor of Easy Effects Sink"
  capturedevicematchsamplerate: 0
  samplerate: 192000
  gain: 1
buffers:
  audiocapturebuffersize: 512
  ringbuffersize: 2097152
  readbuffersize: 16384
  readbufferdelay: 32
  xyoscilloscopereadbuffersize: 8192
  beatdetectreadbuffersize: 1048576
windowfn:
  usekaiserinsteadofhann: true
  kaiserparam: 8
line:
  opacity: 255
  brightness: 1
  thicknessxy: 3
  thicknesssinglechannel: 3
  invsqrtopacitycontrol:
    enable: true
    linsens: 2
		lincutofffrac: 0.02
    uselogdecrement: true
    logbase: 10
    logoffset: 1
  timedependentopacitycontrol:
    enable: true
    base: 0.9997
  opacityalsoaffectsthickness: true
colors:
  useconfigcolorsinsteadofpywal: true
  palette:
    accent: "#C1C7D2"
    first: "#C1C7D2"
    third: "#C1C7D2"
    particle: "#DDE3EE"
    bg: "#080F16"
  bgopacity: 255
  disablebgtransparency: true
imageretention:
  enable: false
  alphadecaybase: 0.1
  alphadecayspeed: 10.0
singlechannelosc:
  displaybuffersize: 8192
  displaydownsample: 8
  iffilteringshowunfiltered: true
  periodcrop:
    enable: false
    displaycount: 2
    loopovercount: 1
  peakdetect:
    enable: true
    peakdetectseparator: 100
    useacf: true
    acfusewindowfn: true
    usemedian: true
    periodrefinementsteps: 4
    triggerthroughoutwindow: true
    usecomplextrigger: true
    aligntolastpossiblepeak: true
    complextriggerusecorrelationtosinewave: true
    fftbufferoffset: 0
    edgeguardbuffersize: 30
    quadratureoffsetpeak: true
    centerpeak: true
  smoothwave:
    enable: true
    invtau: 100
    timeindependent: true
    timeindependentfactor: 0.4
    maxperiods: 10
  traces:
  - thickness: 1
    opacity: 1
    slew:
      enable: true
      accel: 20
      drag: 10
      direct: 20
  - thickness: 0.5
    opacity: 0.5
    slew:
      enable: true
      accel: 15
      drag: 7
      direct: 15
  - thickness: 0.25
    opacity: 0.25
    slew:
      enable: true
      accel: 10
      drag: 5
      direct: 10
  - thickness: 0.125
    opacity: 0.125
    slew:
      enable: true
      accel: 7
      drag: 5
      direct: 7
scale:
  enable: false
  mainaxisenable: true
  textopacity: 255
  mainaxisthickness: 2
  horz:
    textenable: true
    textsize: 10
    textpadding: 5
    tickenable: true
    ticktogrid: false
    gridthickness: 0.5
    tickthickness: 1
    ticklength: 10
    divs: 20
  vert:
    textenable: true
    textsize: 10
    textpadding: 5
    tickenable: true
    ticktogrid: false
    gridthickness: 0.5
    tickthickness: 1
    ticklength: 10
    divs: 20
  horzdivdynamicpos: true
particles:
  enable: false
  geneveryxsamples: 4000
  maxcount: 100
  minsize: 1
  maxsize: 3
  acceleration: 0.2
  drag: 5
bars:
  usewindowfn: true
  preserveparsevalenergy: true
  preventleakageabovefreq: 160000
  width: 0.5
  paddingedge: 4
  paddingbetween: 0
  autogain:
    enable: true
    speed: 0.5
    minvolume: 1e-09
  interpolate:
    enable: true
    accel: 20
    drag: 2
    direct: 20
  peakcursor:
    enable: false
    shownote: true
    refnotefreq: 440
    textsize: 24
    textopacity: 255
    textoffset: -4
    bgwidth: 210
    bgpadding: 2
    interpolatepos:
      enable: true
      accel: 0.1
      drag: 1
      direct: 0.01
  phasecolors:
    enable: false
    lmult: 0.8
    cmult: 3
    hmult: 1
    interpolate:
      enable: true
      accel: 2
      drag: 0.2
      direct: 2
vu:
  paddingbetween: 64
  paddingedge: 8
  preserveparsevalenergy: true
  logscale: true
  logmaxdb: 3
  logmindb: -40
  linmax: 1.1
  interpolate:
    enable: true
    accel: 2
    drag: 10
    direct: 40
  scale:
    enable: true
    textsize: 12
    textoffset: -2
    logdivisions:
    - 3
    - 2
    - 1
    - 0
    - -1
    - -2
    - -3
    - -4
    - -5
    - -6
    - -8
    - -10
    - -15
    - -20
    - -30
    - -40
    lindivisions:
    - 0
    - 0.1
    - 0.2
    - 0.3
    - 0.4
    - 0.5
    - 0.6
    - 0.7
    - 0.8
    - 0.9
    - 1
    - 1.1
    ticksouter: true
    ticksinner: true
    tickthickness: 1
    ticklength: 2
    tickpadding: 2
  peak:
    enable: true
    historyseconds: 5
    interpolate:
      enable: true
      accel: 2
      drag: 10
      direct: 40
    thickness: 2
waterfall:
  speed: 165.
  targetpeakvaldecay: 0.4
  interpolatepeakval:
    enable: true
    direct: 1
    accel: 2
    drag: 10
beatdetection:
  enable: true
  showbpm: true
  bpmtextsize: 24
  intervalms: 100
  downsamplefactor: 4
  bpmcorrectionspeed: 4
  timecorrectionspeed: 0.4
  maxbpm: 500
  halfdisplayedbpm: false
  metronome:
    enable: true
    height: 8
    padding: 8
    edgemode: false
		edgethickness: 0.5
    thinlinemode: true
    thinlinethicknesschangewithvelocity: true
    thinlinethickness: 64
    thinlinehintthickness: 2
filterinfo:
  enable: true
  textsize: 16
  textpaddingleft: 16
  textpaddingbottom: 4
shaders:
  enable: true
  modepresetslist:
  - - 2
    - 4
    - 5
    - 0
  - - 3
    - 6
    - 0
  - - 3
    - 6
    - 0
  - - 3
    - 6
    - 0
  - - 3
    - 6
    - 0
  - - 3
    - 6
    - 0
  presets:
  - - name: glow
      arguments:
        Strength: 0.05
      timescale: 0
    - name: chromaticabberation
      arguments:
        Strength: 0.001
      timescale: 0
  - - name: glow
      arguments:
        Strength: 0.05
      timescale: 0
    - name: gammacorrectionalphafriendly
      arguments:
        MidPoint: 0.1
        Strength: 2.
      timescale: 0
    - name: gammacorrectionalphafriendly
      arguments:
        MidPoint: 0.45
        Strength: 8.
      timescale: 0
    - name: chromaticabberation
      arguments:
        Strength: 0.001
      timescale: 0
  - - name: glow
      arguments:
        Strength: 0.1
      timescale: 0
    - name: gammacorrection
      arguments:
        MidPoint: 0.1
        Strength: 2.
      timescale: 0
    - name: gammacorrection
      arguments:
        MidPoint: 0.45
        Strength: 10.
      timescale: 0
    - name: chromaticabberation
      arguments:
        Strength: 0.001
      timescale: 0
  - - name: glow
      arguments:
        Strength: 0.04
      timescale: 0
    - name: gammacorrection
      arguments:
        MidPoint: 0.1
        Strength: 4.
      timescale: 0
    - name: gammacorrection
      arguments:
        MidPoint: 0.45
        Strength: 8.
      timescale: 0
    - name: chromaticabberation
      arguments:
        Strength: 0.001
      timescale: 0
  - - name: crtcurve
      arguments:
        Strength: 0.5
      timescale: 0
    - name: glow
      arguments:
        Strength: 0.1
      timescale: 0
    - name: gammacorrection
      arguments:
        MidPoint: 0.1
        Strength: 2.
      timescale: 0
    - name: gammacorrection
      arguments:
        MidPoint: 0.45
        Strength: 10.
      timescale: 0
    - name: chromaticabberation
      arguments:
        Strength: 0.001
      timescale: 0
  - - name: crt
      arguments: {}
      timescale: 0
    - name: glow
      arguments:
        Strength: 0.05
      timescale: 0
    - name: gammacorrection
      arguments:
        MidPoint: 0.1
        Strength: 4.
      timescale: 0
    - name: gammacorrection
      arguments:
        MidPoint: 0.45
        Strength: 8.
      timescale: 0
    - name: chromaticabberation
      arguments:
        Strength: 0.001
      timescale: 0
  - - name: crtcurve
      arguments:
        Strength: 0.5
      timescale: 0
    - name: glow
      arguments:
        Strength: 0.04
      timescale: 0
    - name: gammacorrection
      arguments:
        MidPoint: 0.1
        Strength: 4.
      timescale: 0
    - name: gammacorrection
      arguments:
        MidPoint: 0.45
        Strength: 8.
      timescale: 0
    - name: chromaticabberation
      arguments:
        Strength: 0.001
      timescale: 0
  customshadercode:
    noise: "//go:build ignore\n\n//kage:unit pixels\n\npackage main\n\nvar Strength
      float\nvar Time float\nvar Scale float\n\nfunc Fragment(dstPos vec4, srcPos
      vec2, color vec4) vec4 {\n\t\t\tvar clr vec4\n\t\t\tclr = imageSrc2At(srcPos)\n\t\t\tamount
      := abs(cos(sin(srcPos.x*Scale+Time+cos(srcPos.y*Scale+Time)*Scale)*Scale+sin(srcPos.x*Scale+Time)*Scale))
      * Strength\n\t\t\tclr.r += amount\n\t\t\tclr.g += amount\n\t\t\tclr.b += amount\n\t\t\tclr.a
      += amount\n\t\t\treturn clr\n}\n\t\t\t"
mpris:
  enable: false
  texttitleyoffset: 0
  textalbumyoffset: -7
  textdurationyoffset: 0
  textopacity: 255

Example aliases for running the setup seen in the screenshots

xyosc-s-1 = "xyosc -mix -lo=0 -hi=0.0015 -width=650 -height=650 -x=-1332 -y=0 -gain=2 &";
xyosc-s-2 = "xyosc -mix -lo=0.0015 -hi=0.0045 -width=650 -height=650 -x=-666 -y=0 -gain=3 &";
xyosc-s-3 = "xyosc -mix -lo=0.0045 -hi=0.0135 -width=650 -height=650 -x=0 -y=-333 -gain=3 &";
xyosc-s-4 = "xyosc -mix -lo=0.0135 -hi=0.1875 -width=650 -height=650 -x=666 -y=0 -gain=2 &";
xyosc-s-5 = "xyosc -mix -lo=0.1875 -hi=0.9375 -width=650 -height=650 -x=1332 -y=0 -gain=4 &";
xyosc-s-6 = "xyosc -mix -width=650 -height=650 -x=0 -y=333 &";
xyosc-sep = "xyosc-s-1 & xyosc-s-2 & xyosc-s-3 & xyosc-s-4 & xyosc-s-5 & xyosc-s-6";

Screenshots

https://github.com/user-attachments/assets/25415824-34aa-4e46-824f-699a99bb9cec

https://github.com/user-attachments/assets/a527369c-aa43-45d3-b790-9c0956e8d629

https://github.com/user-attachments/assets/8495da4f-dadc-44ab-9f8d-dc0331f0c421

https://github.com/user-attachments/assets/7b6c6545-5a93-4ba8-8a8a-a381d3e62672