Hacker News new | past | comments | ask | show | jobs | submit login
Show HN: Handel – A procedural programming language for writing songs in browser (handel-pl.github.io)
187 points by ddj231 on Jan 5, 2021 | hide | past | favorite | 58 comments

I started building Handel some weeks ago, as a simple way of writing music with code. While the livecoding movement focuses on providing solutions for performing music live with code, and making changes on the fly -- I focused on simple song writing (one reason why I wouldn't consider Handel an esoteric language).

One notable aspect of Handel is the behavior of its procedures, which are called chunks. Each chunk is conceptually a song track and all run chunks (from procedure calls) play at the same time. A run of a chunk can be customized by passing in arguments (ex. chords to use). The bpm, instrument, and loop amount of a run of a chunk can also be optionally set to further customize that run.

Open to questions and feedback!

Hi! I built something similar: https://fergonco.org/MusicJargon/. I think you will find it interesting. It compiles to midi, though. Your browser support is quite a feature, indeed.

Had long discussions with my brother (the family musician) on how to encode chords. At some point I even used the actual UTF characters for sharp and bemol and was a total nightmare to write :D

My effort is mostly abandoned (I switched to teach my kids to actually play!). Courage!

Hi! Compiling to midi is also a great feature. I think having both the immediate feedback of hearing your compositions, and the ability to export to midi would be useful

I suggest you add a drop down with some composition examples in the editor so it kind of demos out what it is capable of. The language feels quite natural to transfer an existing score or to compose something out so it could fill out some niche. I'm not a trained musician so take this as an opinion.

There's an examples folder here linked to from the documentation but having examples in editor would be intuitive as well. (here's the link to the folder if you were curious: https://github.com/ddj231/Handel/tree/master/Examples)

The editor is pretty barebones at the moment

Definitely cool! I do like simple text-based music tools, and the fact that it has a live web player is excellent.

A couple of things I noted:

- I couldn't work out how to run two chunks in sequence - I'm presuming the language doesn't support it yet, since none of the examples do it either?

- It would be nice if the "play" keyword accepted the "loop" syntax too (or something similar)

- On the web editor, despite the song reaching the finish, you have to press stop before you can press play again. It's a little thing, but I spent an embarrassingly long amount of time thinking it was just a firefox bug, copying my song to clipboard, refreshing the page and pasting it back in!

That said, have enjoyed playing with it. Any future plans?

Thanks for the feedback!

Running two chunks in sequence is currently not supported, but I've been considering a separate block (or a customization of a run of a chunk) that could allow that behavior.

Adding looping functionality to play would also be a nice addition.

Future plans wise, in the short term I'm looking to address some bugs and proposed features. But generally want to get the interpreter to a stable state

Syntax is very human readable, yet still expressive.

In terms of extending, is there an easy interface for generating custom waveforms? Or alternatively, if I generate sine oscillator raw data just using javascript math functions, can I mix the resulting waveform into a Handel chuck that's already running?

My first though was that this would make a great basis for Visual Coding! Great Work ;)

Looks great on the first glance! I'll inspect it in detail tonight.

I'm looking for something that helps my get back into chip-tunes composing. I used to be an ImpulseTracker junkie in the past.

Got any songs to share? It's the kind of music I listen to most of the time, specially while coding/working on my projetcs!

Is there a reason you decided to start from scratch with this DSL rather than implement one of the many other live coding DSLs on top of Tone.js ? Some specific vision of the end point, or just a desire to start from the beginning?

I wanted to deviate from live coding, and make a language that targets songwriting/composition. I think the end goal of live coding is slightly different, and seems to target performance (though live coding DSLs and applications can be used to do the former)

Am very interested! By looking at it on a glance, I'm wanting to write a mscx (Musescore) importer. I have a question though, that may have skipped my eyes: is there a way of setting a note's duration to less than a beat?

How do you run two runnables at the same time? Like, chords for the left hand, and then the melody for the right?

On a basic level the program below would accomplish that (can't promise this sounds pleasing to the ear ;) -


chunk lefthand

play C3, Eb3, G3 for 2b

rest for 2b


chunk righthand

play C3 for 1b

play Gb3 for 1b

play Eb3 for 1b

play Db3 for 1b


run lefthand with loop for 100

run righthand with loop for 100


Which part of "run lefthand with loop for 100" makes it non-blocking? It it "run" instead of "play"?

Currently all run chunks play at the same time. So 'run somechunk' will create a run of a chunk that begins at the same time as all other run chunks.

Your question does bring up an interesting idea, which would be to have a 'play somechunk' command that is blocking. But that does not exist at the moment

Do you feel it's made it easier for you to compose? Has it changed the way you think about music?

How is this different (or better than) from ABC notation?

I think in 2 ways:

1. Readability:



play E3, Ab3 for 1b

rest 5b

play E3, Ab3 for 1b


It is somewhat intuitive to read the above without experience with the language (or with programming in general).

2. Procedures: You can create chunks (procedures), which are reusable. For example you can create a chunk which takes 3 chord playables as parameters and run that chunk with any arguments. And also stack those runs on top of each other (because all run chunks play at the same time).

Just FYI: Handel translates to „Trade“ in German. I think you picked it to refer to the composer „Händel”, right? Anyway I expected something different after reading the name ;)

In English he is known as Handel. At least in Dublin anyway where he first performed his Messiah so it is somewhat canonical ...

Also apparently a naturalised British citizen without the umlaut [0]

EDIT check out his signature on [0] no umlaut so he was Handel even to himself!

[0] https://en.m.wikipedia.org/wiki/Messiah_(Handel)

For what its worth, Handel means "trade" in Swedish as well, but I instantly thought of the composer when I saw the name.

Ever tried putting an umlaut in a URL?

Yes, I do that regularly and it works as one would expect. What are you trying to bring up?

Well most of the planet wouldn’t know how if you paid them to

RFC 3987 never really caught on.

See also https://www.w3.org/International/wiki/IRIStatus

A try to JoJo: start chunk introriff play G4, B4, E5 for 16b play C5 for 4b play A#4 for 1b play C5 for 1b play A#4 for 1b play G4 for 5b play F#4 for 8b play A#4 for 12b play E3 for 12b play G4, B4, E5 for 12b play C5 for 4b play A#4 for 1b play C5 for 1b play A#4 for 1b play G4 for 5b play F#4 for 8b play B3 for 1b play C4 for 1b play D4 for 2b play F#3 for 1b play G3 for 1b play A3 for 2b play C3 for 20b play E3 for 12b play G4, B4, E5 for 12b play C5 for 4b play A#4 for 1b play C5 for 1b play A#4 for 2b play G4 for 5b play F#4 for 8b play A#4 for 16b play F#4 for 28b play G3, F#4 for 4b play G4 for 1b play G4 for 1b play F#4 for 1b play E4 for 1b play D4 for 1b play C3, E4 for 2b play D4 for 2b play C4 for 2b play B3 for 2b play B2, D4 for 2b play B3 for 2b play A2, C4 for 2b play A3 for 2b play G2, B3 for 2b play G3 for 2b play F#2, A3 for 2b play B2, E3, G3, B3, E4 for 6b play C#2, F#3, A3, B3, E4 for 2b rest for 2b play D2, G3, B3, B3, E4 for 2b rest for 2b play C#2, F#3, A3, B3, E4 for 2b rest for 2b play D2, G3, B3, B3, E4 for 2b rest for 2b play C#2, F#3, A3, B3, E4 for 2b play B2, E3, G3, B3, E4 for 8b endchunk run introriff with bpm 510, sound piano finish

So cool!

Love it!

I tinkered with it a bit and create this code[0] based on a song[1] that I'm learning. I think this tool is cool and has potential, but still need a lot more improvement. At least it needs a way to reduce duplicate in my code :)

[0]: https://pastebin.com/9ziV0HRe

[1]: https://www.youtube.com/watch?v=3t9iLEkTeAk

I added the loop customization to play commands in Handel v0.1.12.

So for repeating commands you can use this syntax:

play somenotelist for 1b loop for 5

Here is your code using the new feature: https://pastebin.com/bNwUHNaM

To make this more robust, I'll be looking to add looping constructs to allow more customized looping in the future.

Nice one!

I agree, it is still in its early stages.

Looking at your code it seems it would be useful to have a way to repeat a play command without a chunk.

I think something like: " play E4 for 1b rep 5 "

As a way to repeat something 5 times for example (and reduce duplicates) could be a future solution.

Being able to set a key or mode would be nice. Play verse but 3 semitones up. Play melody A but in phrygian. Would be nice to experiment with different moods

Looks interesting. I had spent a while playing around with LilyPond (http://lilypond.org/), and I think something like Handel would solve issues with some of the more repetitive parts of using LilyPond.

One thing that would be helpful that I didn't see would be able to write transformations of chunks. For a few examples: (1) taking a chunk and replaying it in a different scale (or with a different starting note), (2) having the end of a chunk resolve to the tonic up an octave, (3) inverting or chunking chords following common patterns.

It would also be great if instead of just outputting midi, it could also output either to engraved music, or to some other format (e.g., LilyPond) that someone could then use to create a PDF.

Lilypond was created for engraving music (aka "sheet music"). Handel has essentially nothing to do with that at all. Handel is a compositional "tool", Lilypond is a music typesetter. There are all kinds of tools for composing/writing music; Lilypond is essentially a backend for such applications to allow them to generate beautiful sheet music.

Nice. Have you found any good workarounds for getting midi to work in Firefox or is this Chrome only?

It works in my firefox

Well, you're both slightly off. No, midi does not work in Firefox, last time I checked, Mozilla still doesn't like the security of the current API hence it hasn't been implemented.

And no, the website is not using midi at all. Midi is a protocol for sending notes between devices, but there is neither any notes to be sent nor any connections to other devices, this is all local. My guess would be that Handel is using the various Audio APIs only.

I suspect that a lot of HN readers may not be aware that this is just the latest example of DSL's used for "live coding" music.

There are others that have been around for a lot longer and have incorporated many (all?) of the features you will start to long for if you use this sort of tool a lot.

Live-coding is an insanely geeky way to make music, but there are people out there doing quite interesting things with it. I use "interesting" deliberately ... I haven't heard anything that I've actually liked :)

He says elsewhere in this thread that this is specifically not live coding. It's more geared towards composition then playback.

How des this compare to Lilypond (aside for being browser based)?

This is really cool. I like domain languages like this a lot. I basically hand rolled a very primitive version of this for a game I made. I wonder if game devs in general would find it useful for prototyping, game jams, etc.

Just FYI to the author, the code samples have dead scrollbars, due to pre elements having overflow: scroll set


That's an interesting use case -- though the language/interpreter is still in its early stages.

And nice catch on the scroll bars, will fix, thanks!

This looks wonderful.

I may be missing it from the description, but when I think "procedural" I think that I should be able to change the variables with math, or at the very least increment variables. Are there any examples of that?

For example, I would think I could play a scale with something like

    save myPlayable = C1 for 1b
      play myPlayable
    while (myPlayable.note < C2)


So by procedural what is meant is that the there are chunks (which are Handel's procedures/functions).

Currently your code snippet can't be replicated in Handel. Looping is accomplished by telling a chunk to loop (giving it a number of times to loop). ex. run somechunk with loop for 100

But there is no arithmetic in the language. Adding arithmetic to playables like you mentioned could be a nice feature

Thinking a little more, I guess I would actually expect it to look a bit more like

    save myNote as C1
    save myPlayable = myNote for 1b
      play myPlayable
      save myNote as myNote + 1
    while (myNote < C2)
(That is, the dot notation looked out of place given your language.)

This reminds me of the very fun and oldschool tracker paradigm for music production. Incidentally, this is experiencing a bit of a renaissance due to the wonderful Polyend Tracker hardware - I also recommend Renoise / Redux, which is probably the most powerful tracker available right now.

I'm having a hard time getting instruments to synchronize in the examples—over time all the sequences drift. Is this expected / known?

It isn't expected. But I have noticed the issue as well, and I'm looking to fix. I think it may have to do with how note lengths from beats are being calculated.

The Handel interpreter uses Tone.js for scheduling and playing notes. But because there is only one Tone.Transport per page, changing the bpm of the Transport would change it for all tracks.

So instead of using Tone.js for setting bpms, the note lengths are calculated based on the bpm and amount of beats, and scheduled in seconds. Which could lead to greater imprecision over time

Would be great if this could interface with Max, pure data, supercollider or some synthesis language.

Looks intriguing but is there no examples of a song or tune fully written?

There are no recreations of songs fully written as of yet (but I do think that is a good idea and something to add to the examples folder).

For now there are a couple examples in the examples folder here: https://github.com/ddj231/Handel/tree/master/Examples

that demo the current core language features.

Functional and very simple to use, and looks clean to the eye. Great job!

Yeah! It seems a nice one, for real... I have many friends that were searching for it, I'll spread the word for sure. Thank you!


reminds me of sonicpi

Sonic PI on the web, with repl.it like forking/remix would be really something.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact