Controlling Hydra visuals with midi_cc function

Hi Everyone,

I’ve been looking around for ways to control Hydra visuals with Sonic Pi. I was mostly focused on OSC but came across a way to send midi_cc messages that seems to work easier. It involves using webMidi on the Hydra side to receive incoming midi cc messages and then you can just send that info from Sonic Pi using the midi_cc function.

First, you need to go to this page and copy the console script:

// register WebMIDI
    .then(onMIDISuccess, onMIDIFailure);

function onMIDISuccess(midiAccess) {
    var inputs = midiAccess.inputs;
    var outputs = midiAccess.outputs;
    for (var input of midiAccess.inputs.values()){
        input.onmidimessage = getMIDIMessage;

function onMIDIFailure() {
    console.log('Could not access your MIDI devices.');

//create an array to hold our cc values and init to a normalized value
var cc=Array(128).fill(0.5)

getMIDIMessage = function(midiMessage) {
    var arr =    
    var index = arr[1]
    //console.log('Midi received on cc#' + index + ' value:' + arr[2])    // uncomment to monitor incoming Midi
    var val = (arr[2]+1)/128.0  // normalize CC values to 0.0 - 1.0

Then go to the Hydra Web Editor and open the browser console. I use Chrome but also tried wtih Firefox and it seemed to work as well. Paste the console script into the console and press enter.

Note: I have altered the following line of code in the console script:

 var val = (arr[2]+1)/128.0  // normalize CC values to 0.0 - 1.0

to this:

 var val = arr[2] 

The original line of code converts the midi_cc messages, which have a range from 0 - 127, to a range of 0 -1. it does this because several parameters for Hydra functions take values between 0 and 1. However, I prefer to just take the values between 0 -127 and then convert the values later in the Hydra code.

Once you have entered the console code into the console, Hydra will be ready to receive incoming midi messages from any source.

On the Sonic Pi side, you can send messages to Hydra using the midi_cc function. The midi_cc function takes two arguments, the first is to specify which control number to send to. This number will also need to be included on the Hydra side. The second number is the value you want to send, which needs to be between 0 -127.


live_loop :mid1 do
  midi_cc 0, rrand_i(3, 6) # sends to control 0 a random integer between 3 and 6
  sleep 1

Hydra code:
(This will pass the random value between 3 and 6 to the shape function which will affect the number of sides of the shape)

shape( () => cc[0]).out() // receives the message from midi_cc 0

As, I said before, several function parameters take a float value between 0 and 1. However, the midi_cc function will round any floating point numbers to the nearest whole number. So if you want to send float values, there are two things you need to do.

On the Sonic Pi end, you can use the val_f: option which will take a floating point value and convert it to a value between 0 - 127 to be consistent with the midi cc values.

live_loop :mid2 do
  midi_cc 1, val_f: rrand(0.2, 0.7) # Converts float values to range of 0 -127
  sleep 0.25

On the Hydra end, when these values are received, it will be received as numbers between 0 and 127 which is not helpful for functions requiring parameters between 0 and 1. So to fix this, you can just divide the cc variable by 127 to convert these values back to the float values.

shape(3, () => cc[1]/127).out() // receives the message from midi_cc 1 and divides it by 127

That is basically all there is to it. Hopefully this can help more people add some visuals to their live coding.

I also put together a video tutorial which goes over how to do all of this:


Good clear tutorial. Thanks. I have usually used OSC with hydra but this is easy to set up using midi controllers.

1 Like

I can’t wait to try this out. Thank you so much for the tutorial

I used:

var val = (arr[2]+1)/128.0 // normalize CC values to 0.0 - 1.0

# 230107 0039 Sonic Pi and Hydra with midi_cc take 01
# Saved 230107 0039

# YT

# How to setup: OBS HD recording
# How to setup: midi_cc

set_volume! 2

live_loop :time, delay: 0.01 do
  sleep 1

live_loop :kick, sync: :time do
  sample :bd_haus, cutoff: 90, amp: [1,0,0,2,0.2].look
  sample :bd_haus, cutoff: 90, amp: [0,1,0,0,0].look, rate: [2,3,4,5,6].choose
  midi_cc 2,[1,0,0,2,0.2].look
  midi_cc 3, [0,1,0,0,0].look
  sleep 0.25

live_loop :hiahat, sync: :time do
  use_synth :noise
  use_synth_defaults release: 0.01, amp: [2,0,rrand_i(0.5,2),1,1,0,0].look, rate: 2
  midi_cc 4,[2,0,rrand_i(0.5,2),1,1,0,0].look
  play :c2
  sleep 0.25

with_fx :ping_pong, mix: 0.7 do
  live_loop :bass, sync: :time do
    use_synth :prophet
    res = range(0,0.9,step: 0.1).look
    notes = [:f2,:f2, :f3, :f2,:fs2].look
    #play [:f2,:f2, :f2, :f2,:fs2].look+0, res: res
    play notes+0, res: res
    #sleep [1,1,1,1,1].look*2
    sleep [1,0.125,1,0.5,1].look*2*2
    puts midi_cc 1,[1,0.125,1,0.5,1].look*2*2

with_fx :reverb, room: 0.9 do
  live_loop :pianoarp, sync: :time do
    use_synth :piano
    use_synth_defaults release: [0.1,0.04,0.08].choose #+0.1 #+0.2
    / chord 1 works/
    use_random_seed 1
    a = 7     #5 #5*3 #5 #7
    rotate = 3
    notes =  [:f5,:gs5,:c6,:ds6,:f6].rotate(rotate).look-knit(0,a, 12,a, 24,a, 36,a).look
    #notes = [:f5,:gs5,:c6,:ds6,:f6].look
    play notes
    puts midi_cc 0,notes#-12-12-12-12-12-12
    #play [:f5].look if bools(1,0,1,0,1,1,0).look
    #play [:gs5].look if bools(1,0,1,0,1,1,0).look
    #play [:c6].look if bools(1,0,1,0,1,1,0).look
    #play [:ds6].look if bools(1,0,1,0,1,1,0).look
    #play [:f6].look if bools(1,0,1,0,1,1,0).look
    #play [:f5].look if bools(1,0).look
    #play [:c5,:ds5,:g5].look if spread(5,10).look
    #play :ds5 if spread(1,7).look
    #play :gs4 if spread(7,9).look
    sleep 0.125

# Hydra code YT

By the way the Chrome Console tend to create errors when trying to recreate connection. I dont know how to avoid that, but you can reset Chrome by closing all chrome browsers, I found out.

1 Like

Very nice :+1: :+1:
With this, a web browser is the only software needed along Sonic Pi to have a full music+visuals setup. I’m new to Pi and I’m surprised at how easily achievable this is. I’n eager to try it.

Thought I’d do a colourful hydra sketch using midi links and audio from Sonic Pi to control it. Uses the midi links discussed above.

code for sketch and Sonic Pi is here


Thank you for this tutorial. I did something similar(in Italian) for my students (12-14 yo). I did not tested it yet with them in the classroom, but I think that for kids this is a much more easy way to integrate Sonic pi and Hydra, without the need of installing Atom and Hydra packages. I will improve my tutorial with some of your suggestions, thanks! And I will give you a feedback as soon as our project is finished.

1 Like

Great tutorial!
Thanks @mrbombmusic

Seems like this midi_cc method is working for everyone, but I can’t get it to work on my end.
I tried @mrbombmusic’s code/video on a Windows and on a Mac, on Chrome on both systems (and Firefox/Edge on Windows only). On both ends, I get stuck here:

And I get no console activity when I uncomment this line

//console.log('Midi received on cc#' + index + ' value:' + arr[2]) // uncomment to monitor incoming Midi

I also tried some of the other codes on this thread, no luck.

My hypothesis: The problem rests on Hydra/Chrome’s side and not on Sonic Pi, as I downloaded a Midi Viewer and I see Sonic Pi is successfully sending out data to the specified channel.

Not trying to turn this thread into a troubleshooting session but I thought it would be worth reporting to see if other people are running into the same issue.

My question to the thread is: Do you see anything on the Sonic Pi side’s IO section that would confirm that Web Midi is active and that SP is sending midi there? I have a couple of other MIDI-compatible devices on my Windows and I suspect it could cause problems, but it does not explain why I am running into the same problem on Mac?

I use loopmidi as midi out in Sonic Pi on Windows
So perhaps install loopmidi + reboot Sonic Pi might solve this?

1 Like

That seems to have done the trick. Thank you! :partying_face: