diff --git a/assets/voxygen/audio/sfx.ron b/assets/voxygen/audio/sfx.ron
index 5434e7818a..7deae97637 100644
--- a/assets/voxygen/audio/sfx.ron
+++ b/assets/voxygen/audio/sfx.ron
@@ -75,6 +75,14 @@
             ],
             threshold: 0.25,
         ),
+        SnowRun: (
+            files: [
+                "voxygen.audio.sfx.footsteps.snow_step_1",
+                "voxygen.audio.sfx.footsteps.snow_step_2",
+                "voxygen.audio.sfx.footsteps.snow_step_3",
+            ],
+            threshold: 0.25,
+        ),
         ExperienceGained: (
             files: [
                 // "voxygen.audio.sfx.character.experience_gained_1",
diff --git a/assets/voxygen/audio/sfx/footsteps/snow_step_1.wav b/assets/voxygen/audio/sfx/footsteps/snow_step_1.wav
index 15f1da1ff9..38fb243d2a 100644
--- a/assets/voxygen/audio/sfx/footsteps/snow_step_1.wav
+++ b/assets/voxygen/audio/sfx/footsteps/snow_step_1.wav
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:14b5b2d7867a04886b235902e6655c362a6d7e26d9a7f3ab6935433c3c3a3577
-size 174854
+oid sha256:b5df4056f85354b477c81dfde450542c3684eb11ffc850ddebdd730cae5c16db
+size 116752
diff --git a/assets/voxygen/audio/sfx/footsteps/snow_step_2.wav b/assets/voxygen/audio/sfx/footsteps/snow_step_2.wav
index 1e79239998..28cb9352cc 100644
--- a/assets/voxygen/audio/sfx/footsteps/snow_step_2.wav
+++ b/assets/voxygen/audio/sfx/footsteps/snow_step_2.wav
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:7009a1825d54f68df2c68e1bd1186793de52574d53817f60550558f8ae27ce55
-size 174854
+oid sha256:2d6ebde0ec293ba854987ac4473b3d0db0f83a9c4edfaf281b69b6fe33f49170
+size 116752
diff --git a/assets/voxygen/audio/sfx/footsteps/snow_step_3.wav b/assets/voxygen/audio/sfx/footsteps/snow_step_3.wav
index 1778435063..0e05ef9814 100644
--- a/assets/voxygen/audio/sfx/footsteps/snow_step_3.wav
+++ b/assets/voxygen/audio/sfx/footsteps/snow_step_3.wav
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:f3bf83db6c9d68423ee36f6c3f8e07caf37a40292b05ad730f6e6a964dcea021
-size 131654
+oid sha256:37042b31ca33c07967a8f94dac7cc8f4887e22e812f8baedc774c0adf781049d
+size 87952
diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs
index 494ee2c0ad..0cb9752003 100644
--- a/common/src/terrain/block.rs
+++ b/common/src/terrain/block.rs
@@ -42,6 +42,7 @@ make_case_elim!(
         Wood = 0x40,
         Leaves = 0x41,
         // 0x42 <= x < 0x50 is reserved for future tree parts
+        Snow = 0x50,
 
         // Covers all other cases (we sometimes have bizarrely coloured misc blocks, and also we
         // often want to experiment with new kinds of block without allocating them a
diff --git a/voxygen/src/audio/music.rs b/voxygen/src/audio/music.rs
index 2434904bf3..96b8ecad36 100644
--- a/voxygen/src/audio/music.rs
+++ b/voxygen/src/audio/music.rs
@@ -45,7 +45,7 @@ use common::{
     state::State,
     terrain::{BiomeKind, SitesKind},
 };
-use rand::{prelude::SliceRandom, thread_rng};
+use rand::{prelude::SliceRandom, thread_rng, Rng};
 use serde::Deserialize;
 use std::time::Instant;
 use tracing::warn;
@@ -64,7 +64,7 @@ pub struct SoundtrackItem {
     title: String,
     path: String,
     /// Length of the track in seconds
-    length: f64,
+    length: f32,
     /// Whether this track should play during day or night
     timing: Option<DayPeriod>,
     biomes: Vec<(BiomeKind, u8)>,
@@ -93,13 +93,10 @@ enum PlayState {
 pub struct MusicMgr {
     soundtrack: SoundtrackCollection,
     began_playing: Instant,
-    //began_fading: Instant,
-    next_track_change: f64,
+    next_track_change: f32,
     /// The title of the last track played. Used to prevent a track
     /// being played twice in a row
     last_track: String,
-    /*last_biome: BiomeKind,
-     *playing: PlayState, */
 }
 
 impl MusicMgr {
@@ -108,61 +105,51 @@ impl MusicMgr {
         Self {
             soundtrack: Self::load_soundtrack_items(),
             began_playing: Instant::now(),
-            //began_fading: Instant::now(),
             next_track_change: 0.0,
             last_track: String::from("None"),
-            /*last_biome: BiomeKind::Void,
-             *playing: PlayState::Stopped, */
         }
     }
 
     /// Checks whether the previous track has completed. If so, sends a
     /// request to play the next (random) track
     pub fn maintain(&mut self, audio: &mut AudioFrontend, state: &State, client: &Client) {
-        // Gets the current player biome
-        //let current_biome: BiomeKind = match client.current_chunk() {
-        //    Some(chunk) => chunk.meta().biome(),
-        //    _ => self.last_biome,
+        if let Some(current_chunk) = client.current_chunk() {
+            println!("biome: {:?}", current_chunk.meta().biome());
+            println!("chaos: {}", current_chunk.meta().chaos());
+            println!("alt: {}", current_chunk.meta().alt());
+            println!("temp: {}", current_chunk.meta().temp());
+            println!("tree_density: {}", current_chunk.meta().tree_density());
+            println!("humidity: {}", current_chunk.meta().humidity());
+            println!("cave_alt: {}", current_chunk.meta().cave_alt());
+            //if let Some(position) = client.current_position() {
+            //    println!("player_pos: {:?}", position);
+        }
+        //let player_position = match client.current_position() {
+        //    Some(pos) => pos,
+        //    None => Vec3::default(),
         //};
-
-        //if let Some(current_chunk) = client.current_chunk() {
-        //    println!("biome: {:?}", current_chunk.meta().biome());
-        //    println!("chaos: {}", current_chunk.meta().chaos());
-        //    println!("alt: {}", current_chunk.meta().alt());
-        //    println!("temp: {}", current_chunk.meta().temp());
-        //    println!("tree_density: {}",
-        //     current_chunk.meta().tree_density());
-        //     println!("humidity: {}", current_chunk.meta().humidity());
-        //    println!("cave_alt: {}", current_chunk.meta().cave_alt());
-        //    if let Some(position) = client.current_position() {
-        //        println!("player_alt: {}", position[2]);
-        //    }
-        //}
+        //let block_position = Vec3::new(
+        //    player_position[0],
+        //    player_position[1],
+        //    player_position[2] - 1.0,
+        //)
+        //.map(|x| x as i32);
+        //let block_kind = match state.get_block(block_position) {
+        //    Some(block) => block.kind(),
+        //    None => BlockKind::Air,
+        //};
+        //println!("BlockKind: {:?}", block_kind);
 
         if audio.music_enabled()
             && !self.soundtrack.tracks.is_empty()
-            && self.began_playing.elapsed().as_secs_f64() > self.next_track_change
-        //        || self.playing == PlayState::Stopped)
-        //    && self.playing != PlayState::FadingOut
+            && self.began_playing.elapsed().as_secs_f32() > self.next_track_change
         {
             self.play_random_track(audio, state, client);
-        //    self.playing = PlayState::Playing;
-        //} else if current_biome != self.last_biome && self.playing == PlayState::Playing {
-        //    audio.fade_out_exploration_music();
-        //    self.began_fading = Instant::now();
-        //    self.playing = PlayState::FadingOut;
-        //} else if self.began_fading.elapsed().as_secs_f64() > 5.0
-        //    && self.playing == PlayState::FadingOut
-        //{
-        //    audio.stop_exploration_music();
-        //    self.playing = PlayState::Stopped;
         }
-        //self.last_biome = current_biome;
     }
 
     fn play_random_track(&mut self, audio: &mut AudioFrontend, state: &State, client: &Client) {
-        //const SILENCE_BETWEEN_TRACKS_SECONDS: f64 = 45.0;
-        const SILENCE_BETWEEN_TRACKS_SECONDS: f64 = 5.0;
+        let silence_between_tracks_seconds: f32 = thread_rng().gen_range(30.0, 60.0);
 
         let game_time = (state.get_time_of_day() as u64 % 86400) as u32;
         let current_period_of_day = Self::get_current_day_period(game_time);
@@ -217,7 +204,7 @@ impl MusicMgr {
         if let Ok(track) = new_maybe_track {
             self.last_track = String::from(&track.title);
             self.began_playing = Instant::now();
-            self.next_track_change = track.length + SILENCE_BETWEEN_TRACKS_SECONDS;
+            self.next_track_change = track.length + silence_between_tracks_seconds;
 
             audio.play_exploration_music(&track.path);
         }
diff --git a/voxygen/src/audio/sfx/event_mapper/block/mod.rs b/voxygen/src/audio/sfx/event_mapper/block/mod.rs
index 8282892a41..991f72987c 100644
--- a/voxygen/src/audio/sfx/event_mapper/block/mod.rs
+++ b/voxygen/src/audio/sfx/event_mapper/block/mod.rs
@@ -71,6 +71,7 @@ impl EventMapper for BlockEventMapper {
             // Condition that must be true to play
             cond: fn(&State) -> bool,
         }
+
         let sounds: &[BlockSounds] = &[
             BlockSounds {
                 blocks: |boi| &boi.leaves,
@@ -108,7 +109,6 @@ impl EventMapper for BlockEventMapper {
                 sfx: SfxEvent::Frog,
                 volume: 0.8,
                 cond: |st| st.get_day_period().is_dark(),
-                //cond: |_| true,
             },
             //BlockSounds {
             //    blocks: |boi| &boi.flowers,
@@ -153,86 +153,34 @@ impl EventMapper for BlockEventMapper {
                     // Get the positions of the blocks of type sounds
                     let blocks = (sounds.blocks)(&chunk_data.blocks_of_interest);
 
-                    //let mut my_blocks = blocks.to_vec();
-                    //// Reduce the number of bird calls from trees
-                    //if sounds.sfx == SfxEvent::Birdcall {
-                    //    my_blocks = my_blocks.choose_multiple(&mut thread_rng(), 6).cloned().collect();
-                    //    //blocks = blocks.to_vec().choose_multiple(&mut thread_rng(), 6).cloned().collect::<Vec<vek::Vec3<i32>>>().as_slice();
-                    //} else if sounds.sfx == SfxEvent::Cricket {
-                    //    my_blocks = my_blocks.choose_multiple(&mut thread_rng(), 6).cloned().collect();
-                    //}
-
                     let absolute_pos: Vec3<i32> =
                         Vec3::from(chunk_pos * TerrainChunk::RECT_SIZE.map(|e| e as i32));
 
                     // Iterate through each individual block
                     for block in blocks {
-
-                    if (sounds.sfx == SfxEvent::Birdcall || sounds.sfx == SfxEvent::Owl) && thread_rng().gen_bool(0.999) {
-                        continue;
-                    }
+                        if (sounds.sfx == SfxEvent::Birdcall || sounds.sfx == SfxEvent::Owl)
+                            && thread_rng().gen_bool(0.999)
+                        {
+                            continue;
+                        }
                         let block_pos: Vec3<i32> = absolute_pos + block;
                         let state = self.history.entry(block_pos).or_default();
 
-                        // Convert to f32 for sfx emitter
-                        //let block_pos = Vec3::new(
-                        //    block_pos[0] as f32,
-                        //    block_pos[1] as f32,
-                        //    block_pos[2] as f32,
-                        //);
                         let block_pos = block_pos.map(|x| x as f32);
 
                         if Self::should_emit(state, triggers.get_key_value(&sounds.sfx)) {
                             // If the camera is within SFX distance
                             if (block_pos.distance_squared(cam_pos)) < SFX_DIST_LIMIT_SQR {
-                                sfx_emitter.emit(SfxEventItem::new(sounds.sfx.clone(), Some(block_pos), Some(sounds.volume)));
+                                sfx_emitter.emit(SfxEventItem::new(
+                                    sounds.sfx.clone(),
+                                    Some(block_pos),
+                                    Some(sounds.volume),
+                                ));
                                 state.time = Instant::now();
                                 state.event = sounds.sfx.clone();
                             }
                         }
                     }
-
-                        //// If the timer for this block is over the spacing
-                        //// and the block is in the history
-                        //if self.history.contains_key(&block_pos) {
-                        //    if self
-                        //        .history
-                        //        .get(&block_pos)
-                        //        .unwrap() // can't fail as the key is in the hashmap
-                        //        .elapsed()
-                        //        .as_secs_f32()
-                        //        > sounds.spacing
-                        //    {
-                        //        // Reset timer for this block
-                        //        self.history.insert(block_pos, Instant::now());
-
-                        //        // Convert to f32 for distance_squared function
-                        //        let block_pos = Vec3::new(
-                        //            block_pos[0] as f32,
-                        //            block_pos[1] as f32,
-                        //            block_pos[2] as f32,
-                        //        );
-
-                        //        // If the camera is within SFX distance
-                        //        if (block_pos.distance_squared(cam_pos)) < SFX_DIST_LIMIT_SQR {
-                        //            // Emit the sound
-                        //            let sfx_trigger_item = triggers.get_trigger(&sounds.sfx);
-                        //            if sfx_trigger_item.is_some() {
-                        //                ecs.read_resource::<EventBus<SfxEventItem>>().emit_now(
-                        //                    SfxEventItem::new(
-                        //                        sounds.sfx.clone(),
-                        //                        Some(block_pos),
-                        //                        Some(sounds.volume),
-                        //                    ),
-                        //                );
-                        //            }
-                        //        }
-                        //    }
-                        //} else {
-                        //    // Start the timer for this block
-                        //    self.history.insert(block_pos, Instant::now());
-                        //}
-                    //}
                 });
             }
         }
@@ -260,21 +208,4 @@ impl BlockEventMapper {
             false
         }
     }
-    //fn map_event(&mut self, blocktype: BlockEmitter) -> Option<SfxEvent> {
-    //    if self.timer.elapsed().as_secs_f32() > 1.0 {
-    //        self.timer = Instant::now();
-    //        let sfx_event = match blocktype {
-    //            BlockEmitter::Leaves => Some(SfxEvent::LevelUp),
-    //            BlockEmitter::Grass => Some(SfxEvent::Roll),
-    //            BlockEmitter::Embers => Some(SfxEvent::Roll),
-    //            BlockEmitter::Beehives => Some(SfxEvent::Roll),
-    //            BlockEmitter::Reeds => Some(SfxEvent::Roll),
-    //            BlockEmitter::Flowers => Some(SfxEvent::Roll),
-    //        };
-
-    //        sfx_event
-    //    } else {
-    //        None
-    //    }
-    //}
 }
diff --git a/voxygen/src/audio/sfx/event_mapper/movement/mod.rs b/voxygen/src/audio/sfx/event_mapper/movement/mod.rs
index b421ecbf50..c0bc9c574b 100644
--- a/voxygen/src/audio/sfx/event_mapper/movement/mod.rs
+++ b/voxygen/src/audio/sfx/event_mapper/movement/mod.rs
@@ -10,7 +10,7 @@ use common::{
     comp::{Body, CharacterState, PhysicsState, Pos, Vel},
     event::EventBus,
     state::State,
-    terrain::TerrainChunk,
+    terrain::{BlockKind, TerrainChunk},
 };
 use hashbrown::HashMap;
 use specs::{Entity as EcsEntity, Join, WorldExt};
@@ -69,38 +69,53 @@ impl EventMapper for MovementEventMapper {
             .filter(|(_, e_pos, ..)| (e_pos.0.distance_squared(cam_pos)) < SFX_DIST_LIMIT_SQR)
         {
             if let Some(character) = character {
-                let state = self.event_history.entry(entity).or_default();
+                let internal_state = self.event_history.entry(entity).or_default();
+
+                // Get the underfoot block
+                let block_position = Vec3::new(pos.0.x, pos.0.y, pos.0.z - 1.0).map(|x| x as i32);
+                let underfoot_block_kind = match state.get_block(block_position) {
+                    Some(block) => block.kind(),
+                    None => BlockKind::Air,
+                };
 
                 let mapped_event = match body {
-                    Body::Humanoid(_) => Self::map_movement_event(character, physics, state, vel.0),
+                    Body::Humanoid(_) => Self::map_movement_event(
+                        character,
+                        physics,
+                        internal_state,
+                        vel.0,
+                        underfoot_block_kind,
+                    ),
                     Body::QuadrupedMedium(_)
                     | Body::QuadrupedSmall(_)
                     | Body::QuadrupedLow(_)
                     | Body::BirdMedium(_)
                     | Body::BirdSmall(_)
-                    | Body::BipedLarge(_) => Self::map_non_humanoid_movement_event(physics, vel.0),
+                    | Body::BipedLarge(_) => {
+                        Self::map_non_humanoid_movement_event(physics, vel.0, underfoot_block_kind)
+                    },
                     _ => SfxEvent::Idle, // Ignore fish, etc...
                 };
 
                 // Check for SFX config entry for this movement
-                if Self::should_emit(state, triggers.get_key_value(&mapped_event)) {
+                if Self::should_emit(internal_state, triggers.get_key_value(&mapped_event)) {
                     sfx_emitter.emit(SfxEventItem::new(
                         mapped_event.clone(),
                         Some(pos.0),
                         Some(Self::get_volume_for_body_type(body)),
                     ));
 
-                    state.time = Instant::now();
+                    internal_state.time = Instant::now();
                 }
 
                 // update state to determine the next event. We only record the time (above) if
                 // it was dispatched
-                state.event = mapped_event;
-                state.on_ground = physics.on_ground;
+                internal_state.event = mapped_event;
+                internal_state.on_ground = physics.on_ground;
                 if physics.in_fluid.is_some() {
-                    state.in_water = true;
+                    internal_state.in_water = true;
                 } else {
-                    state.in_water = false;
+                    internal_state.in_water = false;
                 }
             }
         }
@@ -162,11 +177,10 @@ impl MovementEventMapper {
         physics_state: &PhysicsState,
         previous_state: &PreviousEntityState,
         vel: Vec3<f32>,
+        underfoot_block_kind: BlockKind,
     ) -> SfxEvent {
         // Match run / roll / swim state
-        if physics_state.in_fluid.is_some()
-            //&& physics_state.in_fluid.unwrap() < 2.0 // To control different sound based on depth
-            && vel.magnitude() > 0.1
+        if physics_state.in_fluid.is_some() && vel.magnitude() > 0.1
             || !previous_state.in_water && physics_state.in_fluid.is_some()
         {
             return SfxEvent::Swim;
@@ -178,7 +192,10 @@ impl MovementEventMapper {
             } else if matches!(character_state, CharacterState::Sneak) {
                 SfxEvent::Sneak
             } else {
-                SfxEvent::Run
+                match underfoot_block_kind {
+                    BlockKind::Snow => SfxEvent::SnowRun,
+                    _ => SfxEvent::Run,
+                }
             };
         }
 
@@ -198,9 +215,18 @@ impl MovementEventMapper {
     }
 
     /// Maps a limited set of movements for other non-humanoid entities
-    fn map_non_humanoid_movement_event(physics_state: &PhysicsState, vel: Vec3<f32>) -> SfxEvent {
-        if physics_state.on_ground && vel.magnitude() > 0.1 {
-            SfxEvent::Run
+    fn map_non_humanoid_movement_event(
+        physics_state: &PhysicsState,
+        vel: Vec3<f32>,
+        underfoot_block_kind: BlockKind,
+    ) -> SfxEvent {
+        if physics_state.in_fluid.is_some() && vel.magnitude() > 0.1 {
+            return SfxEvent::Swim;
+        } else if physics_state.on_ground && vel.magnitude() > 0.1 {
+            match underfoot_block_kind {
+                BlockKind::Snow => SfxEvent::SnowRun,
+                _ => SfxEvent::Run,
+            }
         } else {
             SfxEvent::Idle
         }
diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs
index d4e56855bc..cb72910152 100644
--- a/voxygen/src/audio/sfx/mod.rs
+++ b/voxygen/src/audio/sfx/mod.rs
@@ -146,6 +146,7 @@ pub enum SfxEvent {
     Idle,
     Swim,
     Run,
+    SnowRun,
     Roll,
     Sneak,
     Climb,
diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs
index 53ddc1cc73..e543f6e00e 100644
--- a/world/src/block/mod.rs
+++ b/world/src/block/mod.rs
@@ -1,7 +1,7 @@
 use crate::{
     column::{ColumnGen, ColumnSample},
     util::{RandomField, Sampler, SmallCache},
-    IndexRef,
+    IndexRef, CONFIG,
 };
 use common::terrain::{
     structure::{self, StructureBlock},
@@ -67,6 +67,7 @@ impl<'a> BlockGen<'a> {
             // marble_small,
             rock,
             // temp,
+            temp,
             // humidity,
             stone_col,
             ..
@@ -128,7 +129,9 @@ impl<'a> BlockGen<'a> {
             let col = Lerp::lerp(sub_surface_color, surface_color, grass_factor);
             // Surface
             Some(Block::new(
-                if grass_factor > 0.7 {
+                if temp < CONFIG.snow_temp + 0.031 {
+                    BlockKind::Snow
+                } else if grass_factor > 0.7 {
                     BlockKind::Grass
                 } else {
                     BlockKind::Earth
diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs
index 60bf0efe0d..2ec204b473 100644
--- a/world/src/sim/mod.rs
+++ b/world/src/sim/mod.rs
@@ -2303,13 +2303,15 @@ impl SimChunk {
     pub fn get_biome(&self) -> BiomeKind {
         if self.alt < CONFIG.sea_level {
             BiomeKind::Ocean
-        } else if self.alt > 450.0 && self.chaos > 0.4 && self.tree_density < 0.7 {
-            BiomeKind::Mountain
-        } else if self.temp > CONFIG.desert_temp && self.humidity < 0.1 {
-            BiomeKind::Desert
+        } else if self.humidity == 0.0 {
+            BiomeKind::Lake
         } else if self.temp < CONFIG.snow_temp {
             BiomeKind::Snowland
-        } else if self.tree_density > 0.65 && self.humidity > 0.9 && self.temp > 0.8 {
+        } else if self.alt > 450.0 && self.chaos > 0.35 && self.tree_density < 0.7 {
+            BiomeKind::Mountain
+        } else if self.temp > CONFIG.desert_temp && self.humidity < 0.6 {
+            BiomeKind::Desert
+        } else if self.tree_density > 0.65 && self.humidity > 0.7 && self.temp > 0.8 {
             BiomeKind::Jungle
         } else if self.tree_density > 0.65 {
             BiomeKind::Forest