diff --git a/components/micropython/usermodule/mp_media.c b/components/micropython/usermodule/mp_media.c
index 07554e86d8ea2f1c3ef813c56ce13a59bb7df5a7..0e138eb4b76a429948703cb386cb255c9e0a604c 100644
--- a/components/micropython/usermodule/mp_media.c
+++ b/components/micropython/usermodule/mp_media.c
@@ -77,6 +77,17 @@ STATIC mp_obj_t mp_seek_relative(mp_obj_t time) {
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_seek_relative_obj, mp_seek_relative);
 
+STATIC mp_obj_t mp_set_volume(mp_obj_t volume) {
+    st3m_media_set_volume(mp_obj_get_float(volume));
+    return 0;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_set_volume_obj, mp_set_volume);
+
+STATIC mp_obj_t mp_get_volume(void) {
+    return mp_obj_new_float(st3m_media_get_volume());
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_get_volume_obj, mp_get_volume);
+
 STATIC mp_obj_t mp_set(mp_obj_t key, mp_obj_t value) {
     st3m_media_set(mp_obj_str_get_str(key), mp_obj_get_float(value));
     return 0;
@@ -108,6 +119,8 @@ STATIC const mp_rom_map_elem_t globals_table[] = {
     { MP_ROM_QSTR(MP_QSTR_get_time), MP_ROM_PTR(&mp_get_time_obj) },
     { MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&mp_seek_obj) },
     { MP_ROM_QSTR(MP_QSTR_seek_relative), MP_ROM_PTR(&mp_seek_relative_obj) },
+    { MP_ROM_QSTR(MP_QSTR_set_volume), MP_ROM_PTR(&mp_set_volume_obj) },
+    { MP_ROM_QSTR(MP_QSTR_get_volume), MP_ROM_PTR(&mp_get_volume_obj) },
     { MP_ROM_QSTR(MP_QSTR_set), MP_ROM_PTR(&mp_set_obj) },
     { MP_ROM_QSTR(MP_QSTR_get), MP_ROM_PTR(&mp_get_obj) },
     { MP_ROM_QSTR(MP_QSTR_get_string), MP_ROM_PTR(&mp_get_string_obj) },
diff --git a/components/st3m/st3m_media.c b/components/st3m/st3m_media.c
index def69c2204a9e520d6594fe4cd32d568654b4c98..cc01ef87437b7f8a9439e0c50dd52350b1c07f54 100644
--- a/components/st3m/st3m_media.c
+++ b/components/st3m/st3m_media.c
@@ -18,10 +18,10 @@ static int16_t *audio_buffer = NULL;
 //       st3m_audio_player_function
 void bl00mbox_audio_render(int16_t *rx, int16_t *tx, uint16_t len);
 
-static inline int16_t mix_and_clip(int16_t a, int16_t b) {
-    if (a == 0) return b;
+static inline int16_t mix_and_clip(int16_t a, int16_t b, int16_t gain) {
+    if (a == 0 && gain == 4096) return b;
     int32_t val = a;
-    val += b;
+    val += (b * gain) >> 12;
     if (val > 32767) {
         val = 32767;
     } else if (val < -32767) {
@@ -38,7 +38,8 @@ void st3m_media_audio_render(int16_t *rx, int16_t *tx, uint16_t len) {
             (audio_media->audio_r + 1 - AUDIO_BUF_SIZE !=
              audio_media->audio_w)) {
             tx[i] = mix_and_clip(
-                tx[i], audio_media->audio_buffer[audio_media->audio_r++]);
+                tx[i], audio_media->audio_buffer[audio_media->audio_r++],
+                audio_media->volume);
             if (audio_media->audio_r >= AUDIO_BUF_SIZE)
                 audio_media->audio_r = 0;
         }
@@ -101,6 +102,16 @@ void st3m_media_seek_relative(float time) {
     st3m_media_seek((audio_media->position * audio_media->duration) + time);
 }
 
+void st3m_media_set_volume(float volume) {
+    if (!audio_media) return;
+    audio_media->volume = volume * 4096;
+}
+
+float st3m_media_get_volume(void) {
+    if (!audio_media) return 0;
+    return audio_media->volume / 4096.0;
+}
+
 void st3m_media_draw(Ctx *ctx) {
     if (audio_media && audio_media->draw) audio_media->draw(audio_media, ctx);
 }
@@ -211,6 +222,7 @@ int st3m_media_load(const char *path) {
     audio_media->audio_buffer = audio_buffer;
     audio_media->audio_r = 0;
     audio_media->audio_w = 1;
+    audio_media->volume = 4096;
 
     return 1;
 }
diff --git a/components/st3m/st3m_media.h b/components/st3m/st3m_media.h
index 714fa47eee7d78d9757ae1ad8348c20c430babce..a0286395f2e506b89d855ef0a956155aaa7feeed 100644
--- a/components/st3m/st3m_media.h
+++ b/components/st3m/st3m_media.h
@@ -49,6 +49,9 @@ struct _st3m_media {
     // decoder should seek to this relative if not -1, and set it to -1
     float seek;
 
+    // audio volume
+    int volume;
+
     // if set to 1 playback is momentarily stopped but can be resumed,
     // this is toggled by st3m_media_play | st3m_media_pause
     int paused;
@@ -82,6 +85,10 @@ float st3m_media_get_position(void);
 void st3m_media_seek(float position);
 // seek a relative amount of seconds forward or with negative values back
 void st3m_media_seek_relative(float seconds_jump);
+// set audio volume (0.0 - 1.0)
+void st3m_media_set_volume(float volume);
+// get audio volume
+float st3m_media_get_volume(void);
 
 // get decoder specific string or NULL if not existing, free returned value
 //  common values: