diff --git a/README.md b/README.md
index 66d022cfaf1cc73b6eeee3dd46ae5e358c1751d8..6ec0964f02c0ff9ecaf5709d1c37fd5b3ed87d5e 100644
--- a/README.md
+++ b/README.md
@@ -1,19 +1,16 @@
-## current hw functionality
-plays an infinite scripted note sequence and provides a basic micropython repl
-makes extra noise when Crtl+D'd in the repl (importing py libraries works too)
+## current functionality
+micropython repl hooked up, hw functionality partly broken
 
 some fun commands to try:
-'''
+```
 import badge_audio
 #turn on sound
 badge_audio.set_global_volume_dB(-10)
-#turn off all demo oscillators
-badge_audio.dump_all_sources()
 
-import synth
-a=synth.tinysynth(440,1);
+from synth import tinysynth
+a=tinysynth(440,1); # enters decay phase without stop signal
 a.start();
-b=synth.tinysynth(660,0);
+b=tinysynth(660,0); # sustains until stop signal
 b.start();
 b.stop();
 
@@ -26,8 +23,13 @@ del b
 gc.collect()
 badge_audio.count_sources();
 #...don't know how to hook up gc to __del__, maybe wrong approach
+```
 
-'''
+files can be transferred with mpremote, such as:
+```
+mpremote fs cp python_payload/boot.py :boot.py
+mpremote fs cp python_payload/boot_button_demo.py :boot_button_demo.py
+```
 
 ## how to build
 
diff --git a/badge23/audio.c b/badge23/audio.c
index b71d09f92b3ef1cf01bf8df344d8d97b74f9a20d..c642cef51f6d504c34b52b622e9bfdd301630c4e 100644
--- a/badge23/audio.c
+++ b/badge23/audio.c
@@ -24,7 +24,7 @@ static void audio_player_task(void* arg);
 #define DMA_BUFFER_COUNT    2
 #define I2S_PORT 0
 
-static void i2s_init_idk_lol(void){
+static void i2s_init(void){
     
     static const i2s_config_t i2s_config = {
         .mode = I2S_MODE_MASTER | I2S_MODE_TX,
@@ -90,6 +90,7 @@ typedef struct _audio_source_t{
 static audio_source_t * _audio_sources = NULL;
 
 uint16_t add_audio_source(void * render_data, void * render_function){
+    //construct audio source struct
     audio_source_t * src = malloc(sizeof(audio_source_t));
     if(src == NULL) return;
     src->render_data = render_data;
@@ -97,13 +98,29 @@ uint16_t add_audio_source(void * render_data, void * render_function){
     src->next = NULL;
     src->index = 0;
 
-    audio_source_t * audio_source = _audio_sources;
-    if(audio_source == NULL){
-        
+    //handle empty list special case
+    if(_audio_sources == NULL){
         _audio_sources = src;
+        return 0; //only nonempty lists from here on out!
     }
+
+    //searching for lowest unused index
+    audio_source_t * index_source = _audio_sources;
+    while(1){
+        if(src->index == (index_source->index)){
+            src->index++; //someone else has index already, try next
+            index_source = _audio_sources; //start whole list for new index
+        } else {
+            index_source = index_source->next;
+        }
+        if(index_source == NULL){ //traversed the entire list
+            break;
+        }
+    }
+
+    audio_source_t * audio_source = _audio_sources;
+    //append new source to linked list
     while(audio_source != NULL){
-        src->index++;
         if(audio_source->next == NULL){
             audio_source->next = src;
             break;
@@ -125,7 +142,8 @@ void remove_audio_source(uint16_t index){
             } else {
                 start_gap->next = audio_source->next;
             }
-            free(audio_source);
+            vTaskDelay(20 / portTICK_PERIOD_MS); //give other tasks time to stop using
+            free(audio_source); //terrible hack tbh
             break;
         }
         start_gap = audio_source;
@@ -145,10 +163,10 @@ uint16_t count_audio_sources(){
 
 static void _audio_init(void) {
     init_scope(241);
-    //i2s_init_std_duplex();
-    i2s_init_idk_lol();
+    i2s_init();
     //ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
 
+    /*
     for(int i = 0; i < NUM_SYNTH; i++){
         if((i%2) || (!DRUMS_TOP) ){ //bottom leaves
             synths[i].decay_steps = 50;
@@ -193,6 +211,7 @@ static void _audio_init(void) {
         }
         add_audio_source(&(synths[i]), trad_osc);
     }
+    */
     TaskHandle_t handle;
     xTaskCreate(&audio_player_task, "Audio player", 20000, NULL, configMAX_PRIORITIES - 1, &handle);
 }
diff --git a/badge23/captouch.c b/badge23/captouch.c
index 2a133699bb1b49c10273e9574f324c307c605152..c277cf87e535b61f776e43b4a3f2f7a1dfd18d05 100644
--- a/badge23/captouch.c
+++ b/badge23/captouch.c
@@ -162,7 +162,6 @@ void gpio_event_handler(void* arg)
     struct ad714x_chip* chip;
     uint16_t pressed;
     while(true) {
-        /*
         if(xQueueReceive(gpio_evt_queue, &chip, portMAX_DELAY)) {
             ad714x_i2c_read(chip, 9, &pressed, 1);
             ESP_LOGI(TAG, "Addr %x, High interrupt %X", chip->addr, pressed);
@@ -173,13 +172,6 @@ void gpio_event_handler(void* arg)
             if(chip == &chip_bot) pressed_bot = pressed;
             espan_handle_captouch(pressed_top, pressed_bot);
         }
-        */
-        vTaskDelay(1000/portTICK_PERIOD_MS);
-        counter++;
-        pressed = counter & ((1 << 5) - 1);
-        pressed_bot = pressed;
-        pressed_top = pressed;
-        espan_handle_captouch(pressed_top, pressed_bot);
     }
 }
 
diff --git a/badge23/espan.c b/badge23/espan.c
index 19dca30bd9c949bcfeb2310b9cd4f710abb94005..08caaa49f9545c697d6bd231111aecd9bc1f6042 100644
--- a/badge23/espan.c
+++ b/badge23/espan.c
@@ -95,12 +95,12 @@ void os_app_main(void)
     ESP_LOGI(TAG, "I2C initialized successfully");
 
     vTaskDelay(1000 / portTICK_PERIOD_MS);
-    set_global_vol_dB(-90);
+    set_global_vol_dB(0);
 
     audio_init();
     leds_init();
     //display_init();
-    captouch_init();
+    //captouch_init();
 
     mp_hal_stdout_tx_str("task inits done\n\r");
     //play_bootsound();
@@ -115,8 +115,7 @@ void os_app_main(void)
     int i = 0;
     void * asdasd = &i;
     while(1) {
-        gpio_event_handler(asdasd);
-
+        continue;
         i = (i + 1) % 10;
         if(!(i == 2 || i == 8)) continue;
         if(VIB){
diff --git a/micropython/ports/esp32/badge23_mp_tinysynth.c b/micropython/ports/esp32/badge23_mp_tinysynth.c
index a1364c8be21187be658959f922d79dbef22b81b9..39f70a1df9cae65de3f5ec99d7516c2a4f3fa808 100644
--- a/micropython/ports/esp32/badge23_mp_tinysynth.c
+++ b/micropython/ports/esp32/badge23_mp_tinysynth.c
@@ -57,6 +57,13 @@ STATIC mp_obj_t tinysynth_stop(mp_obj_t self_in) {
 }
 MP_DEFINE_CONST_FUN_OBJ_1(tinysynth_stop_obj, tinysynth_stop);
 
+STATIC mp_obj_t tinysynth_freq(mp_obj_t self_in, mp_obj_t freq) {
+    synth_tinysynth_obj_t *self = MP_OBJ_TO_PTR(self_in);
+    self->osc.freq = mp_obj_get_float(freq);
+    return mp_obj_new_float(self->osc.freq);
+}
+MP_DEFINE_CONST_FUN_OBJ_2(tinysynth_freq_obj, tinysynth_freq);
+
 STATIC mp_obj_t tinysynth_deinit(mp_obj_t self_in) {
     synth_tinysynth_obj_t *self = MP_OBJ_TO_PTR(self_in);
     remove_audio_source(self->source_index);
@@ -68,6 +75,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(tinysynth_deinit_obj, tinysynth_deinit);
 STATIC const mp_rom_map_elem_t tinysynth_locals_dict_table[] = {
     { MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&tinysynth_start_obj) },
     { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&tinysynth_stop_obj) },
+    { MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&tinysynth_freq_obj) },
     { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&tinysynth_deinit_obj) },
 };
 
diff --git a/python_payload/boot.py b/python_payload/boot.py
new file mode 100644
index 0000000000000000000000000000000000000000..fbfb2c769a112f46554ef473582c320f02a5fc04
--- /dev/null
+++ b/python_payload/boot.py
@@ -0,0 +1,6 @@
+# This file is executed on every boot (including wake-boot from deepsleep)
+#import esp
+#esp.osdebug(None)
+#import webrepl
+#webrepl.start()
+import boot_button_demo
diff --git a/python_payload/boot_button_demo.py b/python_payload/boot_button_demo.py
new file mode 100644
index 0000000000000000000000000000000000000000..2fe1f6bbef56d0f701d9728248ccfa9d66874b7e
--- /dev/null
+++ b/python_payload/boot_button_demo.py
@@ -0,0 +1,7 @@
+from machine import Pin
+from synth import tinysynth
+p = Pin(0, Pin.IN)
+s = tinysynth(440,1)
+while True:
+    if(p.value() == 0):
+        s.start()