- Go 93.5%
- GLSL 5.1%
- ANTLR 1.2%
- Shell 0.2%
| assets | load fonts from next to binary; calc display size | |
| doc | load image | |
| src | load image | |
| .gitignore | handle multiple textures | |
| build.sh | fixed hh unit tests | |
| LICENSE | add AGPL 3.0 license | |
| notes.txt | load fonts from next to binary; calc display size | |
| README.md | editor canvas is a source in Hydra | |
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 ofMath.sin(time * Math.PI), it's justsin(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:
- go-gl/gl for OpenGL
- go-gl/glfw for running in a Window if you have a Desktop
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)