How to operate rings, like insert/remove/append

for a list I can use insert method to add items into it. how to do the same thing for a ring?


maybe this is what you are looking for:

It is part of the official tutorial so you should be able to open it from within Sonic Pi.


1 Like

(You can also access the tutorial online - so the above is also at

1 Like

Thanks! But I’ve checked out this tutorial before. What I want is something like:
r = (ring :e2)
r = r + :e3
puts r
// (ring :e2,:e3)

seems no solution in the tutorial?

This might be what you are looking for

r = [:e2] #initialize r as an array
r.push(:e3) #push new value into array
puts r # prints [:e2, :e3]

The example below just shows that the array will run as a ring. If it was an array, it wouldn’t repeat the notes in the array.

live_loop :ringDemo do
  play r.ring.tick #runs through array as a ring
  sleep 1

Hope that helps. Just be aware that push is part of Ruby, not Sonic PI, so there is always a chance it might not work at some point.

1 Like

Yes! That exactly is what I am looking for ~ Thanks:)

Hi there,

it’s important to point out that Sonic Pi’s rings are immutable. This means that you can’t change them in any way - which makes them safe to share across threads and live loops :slight_smile:

You can read about the various operations you can perform on rings here: Note that you should always use a new variable to capture the new ring, as the original ring is never changed:

a = (ring 1, 2, 3)
b = a.reverse
# a is not changed!

However, as @mrbombmusic points out, you can use normal arrays which aren’t immutable (and are therefore not sensible to share across threads) and then convert them to a ring with .ring to give you the ring abilities.

Also, rings do support push:

a = (ring 1, 2, 3)
b = a.push 4
puts b #=> (ring 1, 2, 3, 4)
puts a #=> (ring 1, 2, 3)

Note that you have to assign the result of push to a new variable as this does not modify a, but produces a whole new ring :slight_smile:

Happy to answer any further questions you might have.

1 Like

Hi samaaron,

I just tried out your example of push with rings and it did not work. Is this something that has changed in newer versions? This is somewhat of an old thread.

a = (ring 1, 2, 3)
b = a.push 4

This example gives the following error:

Thread death!
undefined method `push` for (ring 1, 2, 3):SonicPi::Core::RingVector

I think it has changed. However, as mentioned above lists are not immutable so you can do this. Convert a to list, push new value, convert to ring.

a = (ring 1, 2, 3)
puts (a.to_a.push 4).ring

gives (ring 1,2,3,4)

1 Like

well wouldn’t it be better to say “convert to an array” than “convert to a list” no ?
to_a converts to an array. a as array.

Then we can use a lot of functions

Sorry but i don’t catch really the difference between list and array… If someone can explain, he is welcomed !

But we can be surprised to see that some array functions work on rings. See reverse that returns a ring with elements reversed.

With spi 3.2.2, we can use look and tick with arrays as we use them with rings, right ? see below.
So as said Robin, the difference you can’t add elements into rings, rings are immutable. So that’s why i can’t push an element into a ring but i can reverse it, that’s it ?

an_array = [:c, :e, :g]
puts an_array
foo =  an_array.reverse()
puts foo

## ringo star
puts ringo = (ring :c2, :e2, :g2, :c3)
# this works
puts ringo = ringo.reverse()

# but push no
# ringo = ringo.push(:c7)

live_loop :play_array_as_a_ring do
  use_synth :piano
  play foo.look
  sleep 0.5

live_loop :play_ringo do
  use_synth :tb303
  play ringo.look
  sleep 0.5

Maybe some clearings are needed :slight_smile:

Yes sorry I’m being a bit lax about terms. I’m using list for 1 dimensional array.
The original example worked for example in SP 2.7 Since them things have been tightened up and you can’t use the example with SP 3.2.2.
Essentially you have to convert the ring to an array (if 1 dimension I have referred to this as a list). Then since this is not immutable you can push an extra value to the end and assign it to a new ring. with the .ring method.

1 Like


a somehow related issue:

A while ago I was looking for a solution to store configurations in lists (some of them 2-dimensional). Based on feedback of @robin.newman (can’t find the thread right now, where this was) I wrote the following helper function to update lists stored and retrieved via the set/get mechanism. This works quite well and might be helpful for others; here is the function and an example. Right now you can only update existing list items (which is what I need as I am changing config lists) but this can be modified to be able to also add or delete items:

# Update item in frozen array (like in: set :lst, [0, 0, 0])
# check which element to be replaced
# last? new value = tail
# first? new value = head
# or somewhere in between ...
# concatenate head [item] tail
# args:
# name (symbol refering to list such as :my_list or simple list)
# pos (position to be updated with ...)
# val (... new value)
define :upd_list do | lst, pos, val |
  #puts "-- upd_list() --"
  #puts "list: #{get(name)}"
  #puts "position: #{pos} | value: #{val}"
  item = [val]
  if lst.is_a? Symbol
    len = get(lst).size
    len = lst.size
  if pos + 1 == len
    tail = []
    if lst.is_a? Symbol
      tail = get(lst)[pos + 1..-1]
      tail = lst[pos + 1..-1]
  if pos == 0
    head = item
    set lst, head + tail
  elsif pos < len
    if lst.is_a? Symbol
      head = get(lst)[0..pos - 1]
      head = lst[0..pos - 1]
    set lst, head + item + tail
  elsif pos >= len
    puts "Error: Position #{pos.to_s} for value #{val.to_s} "
    puts "is not within this list (length: #{len.to_s})."

set :a, [0, 1, 2]
puts get(:a)

upd_list(:a, 2, 4)
puts get(:a)

a = [0, 1, 2]
puts a

b = upd_list(a, 2, 4)
puts b