diff --git a/components/badge23/espan.c b/components/badge23/espan.c
index 0375ab5b45a957a0bf53af9d80148df7f00a9aba..c7b2be6dad26f360dc049ae67dcb32110f22dd11 100644
--- a/components/badge23/espan.c
+++ b/components/badge23/espan.c
@@ -58,7 +58,7 @@ static esp_err_t i2c_master_init(void)
 }
 
 #define CAPTOUCH_POLLING_PERIOD 10
-#define SLOW_SYSTEM_STATUS_PERIOD 200
+#define SLOW_SYSTEM_STATUS_PERIOD 100
 static uint8_t hw_init_done = 0;
 
 void i2c_timer(TimerHandle_t data){
@@ -82,11 +82,13 @@ void slow_system_status_task(void * data){
         xQueueReceive(slow_system_status_queue, &dummy_data, portMAX_DELAY);
         //read out stuff like jack detection, battery status, usb connection etc.
         audio_update_jacksense();
+        leds_update_hardware();
     }
 }
 
 void locks_init(){
     mutex_i2c = xSemaphoreCreateMutex();
+    mutex_LED = xSemaphoreCreateMutex();
 }
 
 void os_app_main(void)
@@ -101,7 +103,6 @@ void os_app_main(void)
     init_buttons();
     captouch_init();
 
-    //vTaskDelay(2000 / portTICK_PERIOD_MS);
     captouch_force_calibration();
 
     display_init();
diff --git a/components/badge23/include/badge23/leds.h b/components/badge23/include/badge23/leds.h
index 361f4ab1ae1bc1c146a5702e1aca6bc720b0c0d8..e36b430a4b9e712cd6f29c1767c235abd43f5fd8 100644
--- a/components/badge23/include/badge23/leds.h
+++ b/components/badge23/include/badge23/leds.h
@@ -1,8 +1,18 @@
 #pragma once
 
 #include <stdint.h>
+#include <stdbool.h>
 void leds_init();
 void leds_set_single_rgb(uint8_t index, uint8_t red, uint8_t green, uint8_t blue);
 void leds_set_single_hsv(uint8_t index, float hue, float sat, float value);
+void leds_set_all_rgb(uint8_t red, uint8_t green, uint8_t blue);
+void leds_set_all_hsv(float hue, float sat, float value);
+void leds_set_brightness(uint8_t brightness);
+uint8_t leds_get_brightness();
+void leds_set_slew_rate(uint8_t slew_rate);
+uint8_t leds_get_slew_rate();
 void leds_update();
-
+void leds_update_hardware(); 
+void leds_set_gamma(float red, float green, float blue); 
+void leds_set_auto_update(bool on);
+bool leds_get_auto_update();
diff --git a/components/badge23/include/badge23/lock.h b/components/badge23/include/badge23/lock.h
index eb52077c7b349d8819a1cec0d181b73ea3f4cd2c..4e597f675651f207f26b11c4b883668957dd8c64 100644
--- a/components/badge23/include/badge23/lock.h
+++ b/components/badge23/include/badge23/lock.h
@@ -4,3 +4,4 @@
 #include <freertos/semphr.h>
 
 SemaphoreHandle_t mutex_i2c;
+SemaphoreHandle_t mutex_LED;
diff --git a/components/badge23/leds.c b/components/badge23/leds.c
index ccd56d565a77d8f40c86cba2d733a2a8f75daf1d..630c91d835c2757f69d380016b73efa69343359f 100644
--- a/components/badge23/leds.c
+++ b/components/badge23/leds.c
@@ -6,14 +6,21 @@
 #include <math.h>
 #include "esp_system.h"
 #include "badge23/leds.h"
+#include "badge23/lock.h"
 #include "badge23_hwconfig.h"
 
+static uint8_t leds_brightness = 69;;
+static uint8_t leds_slew_rate = 255;
+static bool leds_auto_update = 0;
+
+static uint8_t gamma_red[256];
+static uint8_t gamma_green[256];
+static uint8_t gamma_blue[256];
+
 #if defined(CONFIG_BADGE23_HW_GEN_P1)
 #define LED_SPI_PORT
-
-#elif defined(CONFIG_BADGE23_HW_GEN_P3) || defined(CONFIG_BADGE23_HW_GEN_P4)
+#elif defined(CONFIG_BADGE23_HW_GEN_P3) || defined(CONFIG_BADGE23_HW_GEN_P4) || defined(CONFIG_BADGE23_HW_GEN_P6)
 #define LED_ASYNC_PORT
-
 #else
 #error "leds not implemented for this badge generation"
 #endif
@@ -35,6 +42,10 @@ struct RGB
     unsigned char B;
 };
 
+struct RGB led_target[40] = {0,};
+struct RGB led_target_buffer[40] = {0,};
+struct RGB led_hardware_value[40] = {0,};
+
 struct HSV
 {
     double H;
@@ -164,7 +175,7 @@ static int setupSPI()
     return ret;
 }
 
-void set_single_led(uint8_t index, uint8_t c[3]){
+static void set_single_led(uint8_t index, uint8_t c[3]){
     setPixel(&leds, index, c);
 }
 
@@ -180,7 +191,7 @@ static void _leds_init() {
     spiTransObject.tx_buffer = leds.LEDs;
 
 
-    TaskHandle_t handle;
+    //TaskHandle_t handle;
     //xTaskCreate(&leds_task, "LEDs player", 4096, NULL, configMAX_PRIORITIES - 2, &handle);
 }
 #endif
@@ -210,12 +221,53 @@ static void renderLEDs(){
 
 #endif
 
+uint8_t led_get_slew(int16_t old, int16_t new, int16_t slew){
+    if(new > old + slew){
+        return old + slew;
+    } else if(new > old) {
+        return new;
+    }
+    if(new < old - slew){
+        return old - slew;
+    } else if(new < old) {
+        return new;
+    }
+    return old;
+}
+
+static void leds_update_target(){
+    for(int i = 0; i < 40; i++){
+        led_target[i].R = led_target_buffer[i].R;
+        led_target[i].G = led_target_buffer[i].G;
+        led_target[i].B = led_target_buffer[i].B;
+    }
+}
+
+void leds_update_hardware(){ 
+    if(leds_auto_update) leds_update_target();
+    xSemaphoreTake(mutex_LED, portMAX_DELAY);
+    for(int i = 0; i < 40; i++){
+        uint8_t c[3];
+        c[0] = led_target[i].R * leds_brightness/255;
+        c[1] = led_target[i].G * leds_brightness/255;
+        c[2] = led_target[i].B * leds_brightness/255;
+        c[0] = led_get_slew(led_hardware_value[i].R, c[0], leds_slew_rate);
+        c[1] = led_get_slew(led_hardware_value[i].G, c[1], leds_slew_rate);
+        c[2] = led_get_slew(led_hardware_value[i].B, c[2], leds_slew_rate);
+        led_hardware_value[i].R = gamma_red[c[0]];
+        led_hardware_value[i].G = gamma_green[c[1]];
+        led_hardware_value[i].B = gamma_blue[c[2]];
+        int8_t index = i + 3 % 40;
+        set_single_led(index, c);
+    }
+    renderLEDs();
+    xSemaphoreGive(mutex_LED);
+}
+
 void leds_set_single_rgb(uint8_t index, uint8_t red, uint8_t green, uint8_t blue){
-    uint8_t c[3];
-    c[0] = red;
-    c[1] = green;
-    c[2] = blue;
-    set_single_led(index, c);
+    led_target_buffer[index].R = red;
+    led_target_buffer[index].G = green;
+    led_target_buffer[index].B = blue;
 }
  
 void leds_set_single_hsv(uint8_t index, float hue, float sat, float val){
@@ -227,17 +279,71 @@ void leds_set_single_hsv(uint8_t index, float hue, float sat, float val){
     
     rgb = HSVToRGB(hsv);
 
-    uint8_t c[3];
-    c[0] = rgb.R;
-    c[1] = rgb.G;
-    c[2] = rgb.B;
-    set_single_led(index, c);
+    led_target_buffer[index].R = rgb.R;
+    led_target_buffer[index].G = rgb.G;
+    led_target_buffer[index].B = rgb.B;
+}
+
+void leds_set_all_rgb(uint8_t red, uint8_t green, uint8_t blue){
+    for(int i = 0; i<40; i++){
+        leds_set_single_rgb(i, red, green, blue);
+    }
+}
+
+void leds_set_all_hsv(float h, float s, float v){
+    for(int i = 0; i<40; i++){
+        leds_set_single_hsv(i, h, s, v);
+    }
 }
 
 void leds_update(){
-    vTaskDelay(10 / portTICK_PERIOD_MS); //do we...
-    renderLEDs();
-    vTaskDelay(10 / portTICK_PERIOD_MS); //...need these?
+    leds_update_target();
+    leds_update_hardware();
+}
+
+void leds_init(){
+    for(uint16_t i = 0; i<256; i++){
+        gamma_red[i] = i;
+        gamma_green[i] = i;
+        gamma_blue[i] = i;
+    }
+    _leds_init();
 }
 
-void leds_init() { _leds_init(); }
+void leds_set_brightness(uint8_t b){
+    leds_brightness = b;
+}
+
+uint8_t leds_get_brightness(){
+    return leds_brightness;
+}
+
+void leds_set_slew_rate(uint8_t s){
+    leds_slew_rate = s;
+}
+
+uint8_t leds_get_slew_rate(){
+    return leds_slew_rate;
+}
+
+void leds_set_auto_update(bool on){
+    leds_auto_update = on;
+}
+
+bool leds_get_auto_update(){
+    return leds_auto_update;
+}
+
+void leds_set_gamma(float red, float green, float blue){
+    for(uint16_t i = 0; i<256; i++){
+        if(i == 0){
+            gamma_red[i] = 0;
+            gamma_green[i] = 0;
+            gamma_blue[i] = 0;
+        }
+        float step = ((float) i) / 255.;
+        gamma_red[i] = (uint8_t) (254.*(pow(step, red))+1);
+        gamma_green[i] = (uint8_t) (254.*(pow(step, green))+1);
+        gamma_blue[i] = (uint8_t) (254.*(pow(step, blue))+1);
+    }
+}
diff --git a/usermodule/micropython.cmake b/usermodule/micropython.cmake
index 2912379039ec6c1a2f51d127fac5dd12a0c1ee3b..326524e3bb510b6bfa6ff877ab30b44e14eadb3a 100644
--- a/usermodule/micropython.cmake
+++ b/usermodule/micropython.cmake
@@ -6,6 +6,7 @@ add_library(usermod_badge23 INTERFACE)
 
 target_sources(usermod_badge23 INTERFACE
     ${CMAKE_CURRENT_LIST_DIR}/mp_hardware.c
+    ${CMAKE_CURRENT_LIST_DIR}/mp_leds.c
     ${CMAKE_CURRENT_LIST_DIR}/mp_audio.c
     ${CMAKE_CURRENT_LIST_DIR}/mp_badge_link.c
     ${CMAKE_CURRENT_LIST_DIR}/mp_synth.c
diff --git a/usermodule/mp_hardware.c b/usermodule/mp_hardware.c
index 836a7804d4b05e272a6b5eadde715a2169aff5ad..fa7dec9ffb64fc5122e73fa8278314987eb9ef9f 100644
--- a/usermodule/mp_hardware.c
+++ b/usermodule/mp_hardware.c
@@ -12,7 +12,6 @@
 #include "py/runtime.h"
 
 #include "badge23/audio.h"
-#include "badge23/leds.h"
 #include "badge23/captouch.h"
 #include "badge23/display.h"
 #include "badge23/spio.h"
@@ -166,32 +165,6 @@ STATIC mp_obj_t mp_dump_all_sources(size_t n_args, const mp_obj_t *args) {
 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_dump_all_sources_obj, 0, 2, mp_dump_all_sources);
 
 
-STATIC mp_obj_t mp_set_led_rgb(size_t n_args, const mp_obj_t *args) {
-    uint8_t index =  mp_obj_get_int(args[0]);
-    uint8_t red =  mp_obj_get_int(args[1]);
-    uint8_t green =  mp_obj_get_int(args[2]);
-    uint8_t blue =  mp_obj_get_int(args[3]);
-    leds_set_single_rgb(index, red, green, blue);
-    return mp_const_none;
-}
-STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_set_led_rgb_obj, 4, 5, mp_set_led_rgb);
-
-STATIC mp_obj_t mp_set_led_hsv(size_t n_args, const mp_obj_t *args) {
-    uint8_t index =  mp_obj_get_int(args[0]);
-    float hue =  mp_obj_get_float(args[1]);
-    float sat =  mp_obj_get_float(args[2]);
-    float val =  mp_obj_get_float(args[3]);
-    leds_set_single_hsv(index, hue, sat, val);
-    return mp_const_none;
-}
-STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_set_led_hsv_obj, 4, 5, mp_set_led_hsv);
-
-STATIC mp_obj_t mp_update_leds(size_t n_args, const mp_obj_t *args) {
-    leds_update();
-    return mp_const_none;
-}
-STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_update_leds_obj, 0, 2, mp_update_leds);
-
 STATIC mp_obj_t mp_version(void) {
     mp_obj_t str = mp_obj_new_str(badge23_hw_name, strlen(badge23_hw_name));
     return str;
@@ -233,9 +206,6 @@ STATIC const mp_rom_map_elem_t mp_module_hardware_globals_table[] = {
     { MP_ROM_QSTR(MP_QSTR_set_global_volume_dB), MP_ROM_PTR(&mp_set_global_volume_dB_obj) },
     { MP_ROM_QSTR(MP_QSTR_count_sources), MP_ROM_PTR(&mp_count_sources_obj) },
     { MP_ROM_QSTR(MP_QSTR_dump_all_sources), MP_ROM_PTR(&mp_dump_all_sources_obj) },
-    { MP_ROM_QSTR(MP_QSTR_set_led_rgb), MP_ROM_PTR(&mp_set_led_rgb_obj) },
-    { MP_ROM_QSTR(MP_QSTR_set_led_hsv), MP_ROM_PTR(&mp_set_led_hsv_obj) },
-    { MP_ROM_QSTR(MP_QSTR_update_leds), MP_ROM_PTR(&mp_update_leds_obj) },
     { MP_ROM_QSTR(MP_QSTR_display_update), MP_ROM_PTR(&mp_display_update_obj) },
     { MP_ROM_QSTR(MP_QSTR_display_set_backlight), MP_ROM_PTR(&mp_display_set_backlight_obj) },
     { MP_ROM_QSTR(MP_QSTR_version), MP_ROM_PTR(&mp_version_obj) },
diff --git a/usermodule/mp_leds.c b/usermodule/mp_leds.c
new file mode 100644
index 0000000000000000000000000000000000000000..4d9baf698e3dc461386ad5c53f7000253a5c2b3c
--- /dev/null
+++ b/usermodule/mp_leds.c
@@ -0,0 +1,125 @@
+// probably doesn't need all of these idk
+#include <stdio.h>
+#include <string.h>
+
+#include "py/runtime.h"
+#include "py/mphal.h"
+#include "mphalport.h"
+#include "modmachine.h"
+#include "extmod/virtpin.h"
+#include "machine_rtc.h"
+#include "py/builtin.h"
+#include "py/runtime.h"
+
+#include "badge23/leds.h"
+#include "badge23/espan.h"
+#include "badge23_hwconfig.h"
+
+STATIC mp_obj_t mp_leds_set_brightness(mp_obj_t b) {
+    leds_set_brightness(mp_obj_get_int(b));
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_leds_set_brightness_obj, mp_leds_set_brightness);
+
+STATIC mp_obj_t mp_leds_get_brightness() {
+    return mp_obj_new_int(leds_get_brightness());
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_leds_get_brightness_obj, mp_leds_get_brightness);
+
+STATIC mp_obj_t mp_leds_set_auto_update(mp_obj_t on) {
+    leds_set_auto_update(mp_obj_get_int(on));
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_leds_set_auto_update_obj, mp_leds_set_auto_update);
+
+STATIC mp_obj_t mp_leds_get_auto_update() {
+    return mp_obj_new_int(leds_get_auto_update());
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_leds_get_auto_update_obj, mp_leds_get_auto_update);
+
+STATIC mp_obj_t mp_leds_set_gamma(mp_obj_t r, mp_obj_t g, mp_obj_t b) {
+    leds_set_gamma(mp_obj_get_float(r), mp_obj_get_float(g), mp_obj_get_float(b));
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(mp_leds_set_gamma_obj, mp_leds_set_gamma);
+
+STATIC mp_obj_t mp_leds_set_slew_rate(mp_obj_t b) {
+    leds_set_slew_rate(mp_obj_get_int(b));
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_leds_set_slew_rate_obj, mp_leds_set_slew_rate);
+
+STATIC mp_obj_t mp_leds_get_slew_rate() {
+    return mp_obj_new_int(leds_get_slew_rate());
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_leds_get_slew_rate_obj, mp_leds_get_slew_rate);
+
+STATIC mp_obj_t mp_led_set_rgb(size_t n_args, const mp_obj_t *args) {
+    uint8_t index =  mp_obj_get_int(args[0]);
+    uint8_t red =  mp_obj_get_int(args[1]);
+    uint8_t green =  mp_obj_get_int(args[2]);
+    uint8_t blue =  mp_obj_get_int(args[3]);
+    leds_set_single_rgb(index, red, green, blue);
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_led_set_rgb_obj, 4, 4, mp_led_set_rgb);
+
+STATIC mp_obj_t mp_led_set_hsv(size_t n_args, const mp_obj_t *args) {
+    uint8_t index =  mp_obj_get_int(args[0]);
+    float hue =  mp_obj_get_float(args[1]);
+    float sat =  mp_obj_get_float(args[2]);
+    float val =  mp_obj_get_float(args[3]);
+    leds_set_single_hsv(index, hue, sat, val);
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_led_set_hsv_obj,4,4, mp_led_set_hsv);
+
+STATIC mp_obj_t mp_led_set_all_rgb(mp_obj_t r, mp_obj_t g, mp_obj_t b) {
+    uint8_t red =  mp_obj_get_int(r);
+    uint8_t green =  mp_obj_get_int(g);
+    uint8_t blue =  mp_obj_get_int(b);
+    leds_set_all_rgb(red, green, blue);
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(mp_led_set_all_rgb_obj, mp_led_set_all_rgb);
+
+STATIC mp_obj_t mp_led_set_all_hsv(mp_obj_t h, mp_obj_t s, mp_obj_t v) {
+    float hue =  mp_obj_get_float(h);
+    float sat =  mp_obj_get_float(s);
+    float val =  mp_obj_get_float(v);
+    leds_set_all_hsv(hue, sat, val);
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(mp_led_set_all_hsv_obj, mp_led_set_all_hsv);
+
+STATIC mp_obj_t mp_leds_update() {
+    leds_update();
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_leds_update_obj, mp_leds_update);
+
+STATIC const mp_rom_map_elem_t mp_module_leds_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_badge_audio) },
+    { MP_ROM_QSTR(MP_QSTR_set_rgb), MP_ROM_PTR(&mp_led_set_rgb_obj) },
+    { MP_ROM_QSTR(MP_QSTR_set_hsv), MP_ROM_PTR(&mp_led_set_hsv_obj) },
+    { MP_ROM_QSTR(MP_QSTR_set_all_rgb), MP_ROM_PTR(&mp_led_set_all_rgb_obj) },
+    { MP_ROM_QSTR(MP_QSTR_set_all_hsv), MP_ROM_PTR(&mp_led_set_all_hsv_obj) },
+    { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&mp_leds_update_obj) },
+    { MP_ROM_QSTR(MP_QSTR_get_brightness), MP_ROM_PTR(&mp_leds_get_brightness_obj) },
+    { MP_ROM_QSTR(MP_QSTR_set_brightness), MP_ROM_PTR(&mp_leds_set_brightness_obj) },
+    { MP_ROM_QSTR(MP_QSTR_get_auto_update), MP_ROM_PTR(&mp_leds_get_auto_update_obj) },
+    { MP_ROM_QSTR(MP_QSTR_set_auto_update), MP_ROM_PTR(&mp_leds_set_auto_update_obj) },
+    { MP_ROM_QSTR(MP_QSTR_set_gamma), MP_ROM_PTR(&mp_leds_set_gamma_obj) },
+    { MP_ROM_QSTR(MP_QSTR_get_slew_rate), MP_ROM_PTR(&mp_leds_get_slew_rate_obj) },
+    { MP_ROM_QSTR(MP_QSTR_set_slew_rate), MP_ROM_PTR(&mp_leds_set_slew_rate_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(mp_module_leds_globals, mp_module_leds_globals_table);
+
+const mp_obj_module_t mp_module_leds = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t *)&mp_module_leds_globals,
+};
+
+MP_REGISTER_MODULE(MP_QSTR_leds, mp_module_leds);
+