1
0
Fork
You've already forked HeadlessHydra
0
Parser and shader generator replicating the Hydra live coding platform, without a browser
  • Go 93.5%
  • GLSL 5.1%
  • ANTLR 1.2%
  • Shell 0.2%
2026年04月14日 18:52:43 +10:00
assets load fonts from next to binary; calc display size 2026年04月04日 15:07:01 +10:00
doc load image 2026年04月14日 18:52:43 +10:00
src load image 2026年04月14日 18:52:43 +10:00
.gitignore handle multiple textures 2026年02月23日 17:08:43 +01:00
build.sh fixed hh unit tests 2026年04月05日 10:50:47 +10:00
LICENSE add AGPL 3.0 license 2026年02月21日 21:11:39 +01:00
notes.txt load fonts from next to binary; calc display size 2026年04月04日 15:07:01 +10:00
README.md editor canvas is a source in Hydra 2026年04月05日 13:30:12 +10:00

Headless Hydra

An implementation of Olivia Jack's hydra-synth, with no browser or Javascript. It needs no Desktop UI, so it can be used for live coding on minimalist devices. Think of a Rasperry Pi with a Lite OS, or an old smartphone running PostmarketOS. It is written in Go and uses ANTLR for parsing Hydra code.

Running example 15 on a CRT television from a Raspberry P
Rendering Example 15 on a Raspberry Pi 3B connected to a CRT television over composite video

How does it work?

The core package is the hh library, whose purpose is to parse Hydra source code and generate .glsl shaders and stack machines. The shaders (up to 4 for the four outputs) can be executed to render the animation. The stack machines encode any calculations that rely on runtime values like time, audio, or mouse position. hh contains these main components:

  • A definition of Hydra's grammar in ANTLR's formalism, and the parser code generated from it
  • Code that interprets the parse tree and converts it into an abstract syntax tree (AST)
  • Code that takes an AST and generates the shaders and stack machines

hh-player is what puts animations on the screen. Its desktop version opens a simple window. Its headless version takes control of the display in full-screen mode. It then renders the shaders in a loop and draws the selected texture on-screen.

hh-player watches a file (by default, ~/.headless-hydra/live.txt) and if it changes, it parses its content and updates the shaders and calculations.

You can use any editor to write updates to live.txt, but a dedicated script for Emacs is provided in hydra.el. This includes syntax highlighting, and key bindings to apply changes. C-j submits the entire buffer (Emacs-speak for file). C-k submits the section around the cursor, up to the nearest empty lines. This lets you edit any file with the .hydra extension, and each time you submit a change, Emacs will write it to ~/.headless-hydra/live.txt for you.

hh-eval is a small utility that reads input from the console, parses it, and outputs the result. It expects one statement at a time and gives you the parse tree, the syntax tree, and the generated shader - or the first error in the input.

Differences from Hydra

Because Headless Hydra does not rely on Javascript, it seemed like a good idea to use a syntax that is simpler in a few small ways.

  • oN = instead of .out(N). The original out() function is a bit special because it doesn't add to a synth, only specifies its output texture. Headless Hydra deviates from this and states the ouput texture at the start of statements. Instead of this:

    osc(20, .2, 0.4).out(0)
    

    HH uses this:

    o0 = osc(20, .2, 0.4)
    
  • Similarly, if you want to use a texture as a source, you don't need to write out src(). Instead of this:

    modulate(src(o0).rotate(1))
    

    HH uses this:

    modulate(o0.rotate(1))
    
  • No need for lambda functions in arguments that use time etc. Instead of this:

    rotate(0, () => Math.sin(time) * 0.01)
    

    HH uses this:

    rotate(0, sin(time) * 0.01)
    
  • The math functions and constants you need are available "naked", without the need for the Math. prefix. So instead of Math.sin(time * Math.PI), it's just sin(time * PI).

How to build and use

Dependencies

You can build the two executables, hh-eval and hh-player, if you have Go 1.25 or higher installed on your system.

The viewer's direct dependencies are:

Indirectly the viewer depends on the following libraries for the embedded terminal emulator:

The evaluator indirectly depends on antlr4-go/antlr/v4.

Go downloads dependencies automatically when you build the programs. You can get Go from here: go.dev/doc/install

Building

In the repository root, run:

./build.sh

Live coding with Headless Hydra (and Emacs)

Start hh-player. It shows a window and watches ~/.headless-hydra/live.txt for changes. Whenever live.txt changes, hh-player parses and executes it as Hydra code. You can save changes to this file manually, or you can use the provided config for Emacs. This config defines a "hydra" mode, which will get activated whenever you are editing a file with the .hydra extension.

In this mode, Emacs will save the full sketch into live.txt when you press C-j, or the current section (in between empty lines) when you press C-k.

To use the config in Emacs:

  • Copy it to ~/.emacs.d
  • Add this to ~/emacs.d/init.el: (load-file "~/.emacs.d/hydra.el")

Screenshot of Emacs and Headless Hydra

Emacs inside Headless Hydra

(This section is WIP)

~/emacs.d/init.el:

;; Hide menu bar at top
(menu-bar-mode -1)
;; Hide mode line at bottom
(setq-default mode-line-format nil)

Building for headless Linux

The version of the viewer that uses GLFW to show a window requires a desktop UI like Mac OS or X11. But you can also run Headless Hydra on a system with no window server! This does need some libraries to be installed on the system. On Debian, use this incantation:

sudo apt install -y build-essential git cmake pkg-config clang-format
sudo apt install -y libegl1-mesa-dev libgbm-dev libdrm-dev

Then build hh-player like so:

go build -C src/hh-player -tags=cli -o ../../bin/hh-player

Or, using the build script in the repository root:

./build.sh cli

The first time you run this, it will take VERY LONG. (In my case, easily above 10 minutes on a Raspberry Pi 3B.) Subsequent builds are also slow by Go standards, but not as slow as the first time.

A note on the ANTLR parser

ANTLR is a parser generator that creates Go code from the grammar described in src/hh/grm/hydra.g4. Although the resulting .go files are generated, I decided to put them under version control so you can build Headless Hydra without installing ANTLR. If you want to mess with the grammar, you need to install ANTLR first. Assuming your system has Python, the simplest way to do it is this:

pip install antlr4-tools
antlr4

Yes, it's odd, but it comes from ANTLR's own Getting Started page.

When this is done, this command (from the repository root) will re-generate the parser:

antlr4 -Dlanguage=Go -package grm src/hh/grm/hydra.g4

Work log

  • Parse expressions with ANTLR into syntax tree
  • Interactive evaluator
  • Generate simple shaders
  • Unit tests for parser (valid inputs)
  • GLFW viewer
  • Generate shaders for blend and modulate commands
  • Implement all Hydra commands
  • Headless viewer (SDF/DRM/GBM stack)
  • Watch file for live reload
  • Hydra mode for Emacs
  • Convert math expressions into stack machines
  • Control commands: render(oN), hush()
  • Parse sketches with multiple statements
  • TTY emulator; use console editor as video source
  • Broaden parser unit tests (valid inputs)
  • Unit tests for parser (invalid inputs)
  • Unit tests for math expressions valid + invalid
  • Synth declarations
  • Mouse input
  • Audio input
  • Source form images
  • Source from video file
  • Rhythms (array extensions in JS)