diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2df9c850c5..ac92f5d9f7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 - New Skills for Climbing: Climbing Speed and Climbing Cost
 - Pickaxes (can be used to collect gems and mine weak rock)
+- You can now jump out of rolls for a slight jump boost
 
 ### Changed
 
diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs
index 25a8433b14..4ca98f1591 100644
--- a/common/src/comp/ability.rs
+++ b/common/src/comp/ability.rs
@@ -1217,6 +1217,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
                     recover_duration: Duration::from_secs_f32(*recover_duration),
                     roll_strength: *roll_strength,
                     immune_melee: *immune_melee,
+                    ability_info,
                 },
                 timer: Duration::default(),
                 stage_section: StageSection::Buildup,
diff --git a/common/src/states/basic_aura.rs b/common/src/states/basic_aura.rs
index d106b74578..feb0c5ba47 100644
--- a/common/src/states/basic_aura.rs
+++ b/common/src/states/basic_aura.rs
@@ -50,7 +50,7 @@ impl CharacterBehavior for Data {
         let mut update = StateUpdate::from(data);
 
         handle_move(data, &mut update, 0.8);
-        handle_jump(data, &mut update);
+        handle_jump(data, &mut update, 1.0);
 
         match self.stage_section {
             StageSection::Buildup => {
diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs
index 93c3c877cc..567340cf27 100644
--- a/common/src/states/basic_beam.rs
+++ b/common/src/states/basic_beam.rs
@@ -70,7 +70,7 @@ impl CharacterBehavior for Data {
         }
 
         handle_move(data, &mut update, 0.4);
-        handle_jump(data, &mut update);
+        handle_jump(data, &mut update, 1.0);
 
         match self.stage_section {
             StageSection::Buildup => {
diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs
index 78719faec7..e27f5fec23 100644
--- a/common/src/states/basic_melee.rs
+++ b/common/src/states/basic_melee.rs
@@ -51,7 +51,7 @@ impl CharacterBehavior for Data {
         let mut update = StateUpdate::from(data);
 
         handle_move(data, &mut update, 0.7);
-        handle_jump(data, &mut update);
+        handle_jump(data, &mut update, 1.0);
 
         match self.stage_section {
             StageSection::Buildup => {
diff --git a/common/src/states/basic_ranged.rs b/common/src/states/basic_ranged.rs
index 501f92d3a6..0565d37ff9 100644
--- a/common/src/states/basic_ranged.rs
+++ b/common/src/states/basic_ranged.rs
@@ -44,7 +44,7 @@ impl CharacterBehavior for Data {
         let mut update = StateUpdate::from(data);
 
         handle_move(data, &mut update, 0.3);
-        handle_jump(data, &mut update);
+        handle_jump(data, &mut update, 1.0);
 
         match self.stage_section {
             StageSection::Buildup => {
diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs
index 049b7374e8..16c55e5629 100644
--- a/common/src/states/charged_melee.rs
+++ b/common/src/states/charged_melee.rs
@@ -67,7 +67,7 @@ impl CharacterBehavior for Data {
         let mut update = StateUpdate::from(data);
 
         handle_move(data, &mut update, 0.7);
-        handle_jump(data, &mut update);
+        handle_jump(data, &mut update, 1.0);
 
         match self.stage_section {
             StageSection::Charge => {
diff --git a/common/src/states/charged_ranged.rs b/common/src/states/charged_ranged.rs
index 909f5056f6..b62b18f5ef 100644
--- a/common/src/states/charged_ranged.rs
+++ b/common/src/states/charged_ranged.rs
@@ -67,7 +67,7 @@ impl CharacterBehavior for Data {
         let mut update = StateUpdate::from(data);
 
         handle_move(data, &mut update, self.static_data.move_speed);
-        handle_jump(data, &mut update);
+        handle_jump(data, &mut update, 1.0);
 
         match self.stage_section {
             StageSection::Buildup => {
diff --git a/common/src/states/dance.rs b/common/src/states/dance.rs
index 2966600bfd..cc5242f316 100644
--- a/common/src/states/dance.rs
+++ b/common/src/states/dance.rs
@@ -13,7 +13,7 @@ impl CharacterBehavior for Data {
         let mut update = StateUpdate::from(data);
 
         handle_wield(data, &mut update);
-        handle_jump(&data, &mut update);
+        handle_jump(data, &mut update, 1.0);
 
         // Try to Fall/Stand up/Move
         if !data.physics.on_ground || data.inputs.move_dir.magnitude_squared() > 0.0 {
diff --git a/common/src/states/equipping.rs b/common/src/states/equipping.rs
index bc08b55170..9d4d007f25 100644
--- a/common/src/states/equipping.rs
+++ b/common/src/states/equipping.rs
@@ -26,8 +26,8 @@ impl CharacterBehavior for Data {
     fn behavior(&self, data: &JoinData) -> StateUpdate {
         let mut update = StateUpdate::from(data);
 
-        handle_move(&data, &mut update, 1.0);
-        handle_jump(&data, &mut update);
+        handle_move(data, &mut update, 1.0);
+        handle_jump(data, &mut update, 1.0);
 
         if self.timer < self.static_data.buildup_duration {
             // Draw weapon
diff --git a/common/src/states/glide_wield.rs b/common/src/states/glide_wield.rs
index ffc1568d64..90a6627b67 100644
--- a/common/src/states/glide_wield.rs
+++ b/common/src/states/glide_wield.rs
@@ -10,8 +10,8 @@ impl CharacterBehavior for Data {
     fn behavior(&self, data: &JoinData) -> StateUpdate {
         let mut update = StateUpdate::from(data);
 
-        handle_move(&data, &mut update, 1.0);
-        handle_jump(&data, &mut update);
+        handle_move(data, &mut update, 1.0);
+        handle_jump(data, &mut update, 1.0);
         handle_dodge_input(data, &mut update);
         handle_wield(data, &mut update);
 
diff --git a/common/src/states/healing_beam.rs b/common/src/states/healing_beam.rs
index bfaae55b08..96ca50b7b5 100644
--- a/common/src/states/healing_beam.rs
+++ b/common/src/states/healing_beam.rs
@@ -53,7 +53,7 @@ impl CharacterBehavior for Data {
         let mut update = StateUpdate::from(data);
 
         handle_move(data, &mut update, 0.4);
-        handle_jump(data, &mut update);
+        handle_jump(data, &mut update, 1.0);
 
         match self.stage_section {
             StageSection::Buildup => {
diff --git a/common/src/states/idle.rs b/common/src/states/idle.rs
index a32728844e..00f683f809 100644
--- a/common/src/states/idle.rs
+++ b/common/src/states/idle.rs
@@ -11,7 +11,7 @@ impl CharacterBehavior for Data {
         let mut update = StateUpdate::from(data);
 
         handle_move(data, &mut update, 1.0);
-        handle_jump(data, &mut update);
+        handle_jump(data, &mut update, 1.0);
         handle_wield(data, &mut update);
         handle_climb(data, &mut update);
         handle_dodge_input(data, &mut update);
diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs
index 50735eaf74..cba0fd7718 100644
--- a/common/src/states/leap_melee.rs
+++ b/common/src/states/leap_melee.rs
@@ -57,7 +57,7 @@ impl CharacterBehavior for Data {
         let mut update = StateUpdate::from(data);
 
         handle_move(data, &mut update, 0.3);
-        handle_jump(data, &mut update);
+        handle_jump(data, &mut update, 1.0);
 
         match self.stage_section {
             // Delay before leaping into the air
diff --git a/common/src/states/repeater_ranged.rs b/common/src/states/repeater_ranged.rs
index ad7280d651..56ab2cadee 100644
--- a/common/src/states/repeater_ranged.rs
+++ b/common/src/states/repeater_ranged.rs
@@ -52,7 +52,7 @@ impl CharacterBehavior for Data {
         let mut update = StateUpdate::from(data);
 
         handle_move(data, &mut update, 1.0);
-        handle_jump(data, &mut update);
+        handle_jump(data, &mut update, 1.0);
 
         match self.stage_section {
             StageSection::Movement => {
diff --git a/common/src/states/roll.rs b/common/src/states/roll.rs
index d96cdddee1..abbffba3bd 100644
--- a/common/src/states/roll.rs
+++ b/common/src/states/roll.rs
@@ -21,6 +21,8 @@ pub struct StaticData {
     pub roll_strength: f32,
     /// Affects whether you are immune to melee attacks while rolling
     pub immune_melee: bool,
+    /// Information about the ability
+    pub ability_info: AbilityInfo,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
@@ -94,17 +96,25 @@ impl CharacterBehavior for Data {
                         ..*self
                     });
                 } else {
-                    // Transitions to recover section of stage
-                    update.character = CharacterState::Roll(Data {
-                        timer: Duration::default(),
-                        stage_section: StageSection::Recover,
-                        ..*self
-                    });
+                    // Keeps rolling if sufficient energy, else transitions to recover section of
+                    // stage
+                    if input_is_pressed(data, self.static_data.ability_info.input) {
+                        reset_state(self, data, &mut update);
+                    } else {
+                        update.character = CharacterState::Roll(Data {
+                            timer: Duration::default(),
+                            stage_section: StageSection::Recover,
+                            ..*self
+                        });
+                    }
                 }
             },
             StageSection::Recover => {
-                if self.timer < self.static_data.recover_duration {
-                    // Build up
+                // Allows for jumps to interrupt recovery in roll
+                if self.timer < self.static_data.recover_duration
+                    && !handle_jump(data, &mut update, 1.5)
+                {
+                    // Recover
                     update.character = CharacterState::Roll(Data {
                         timer: self
                             .timer
@@ -143,3 +153,19 @@ impl CharacterBehavior for Data {
         update
     }
 }
+
+fn reset_state(data: &Data, join: &JoinData, update: &mut StateUpdate) {
+    handle_input(join, update, data.static_data.ability_info.input);
+
+    if let CharacterState::Roll(r) = &mut update.character {
+        r.was_combo = data.was_combo;
+        r.was_sneak = data.was_sneak;
+        r.was_wielded = data.was_wielded;
+        if matches!(r.stage_section, StageSection::Movement) {
+            r.timer = Duration::default();
+            r.stage_section = StageSection::Recover;
+        } else {
+            r.stage_section = StageSection::Movement;
+        }
+    }
+}
diff --git a/common/src/states/sit.rs b/common/src/states/sit.rs
index 5091e0be45..459cba1ac6 100644
--- a/common/src/states/sit.rs
+++ b/common/src/states/sit.rs
@@ -13,7 +13,7 @@ impl CharacterBehavior for Data {
         let mut update = StateUpdate::from(data);
 
         handle_wield(data, &mut update);
-        handle_jump(&data, &mut update);
+        handle_jump(data, &mut update, 1.0);
 
         // Try to Fall/Stand up/Move
         if !data.physics.on_ground || data.inputs.move_dir.magnitude_squared() > 0.0 {
diff --git a/common/src/states/sneak.rs b/common/src/states/sneak.rs
index 03fe51772a..4016cef6cf 100644
--- a/common/src/states/sneak.rs
+++ b/common/src/states/sneak.rs
@@ -11,7 +11,7 @@ impl CharacterBehavior for Data {
         let mut update = StateUpdate::from(data);
 
         handle_move(data, &mut update, 0.4);
-        handle_jump(data, &mut update);
+        handle_jump(data, &mut update, 1.0);
         handle_wield(data, &mut update);
         handle_climb(data, &mut update);
         handle_dodge_input(data, &mut update);
diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs
index 225a36d59d..53d4f1693d 100644
--- a/common/src/states/utils.rs
+++ b/common/src/states/utils.rs
@@ -455,7 +455,7 @@ pub fn attempt_glide_wield(data: &JoinData, update: &mut StateUpdate) {
 }
 
 /// Checks that player can jump and sends jump event if so
-pub fn handle_jump(data: &JoinData, update: &mut StateUpdate) {
+pub fn handle_jump(data: &JoinData, update: &mut StateUpdate, strength: f32) -> bool {
     if input_is_pressed(data, InputKind::Jump)
         && data.physics.on_ground
         && !data
@@ -467,8 +467,11 @@ pub fn handle_jump(data: &JoinData, update: &mut StateUpdate) {
     {
         update.local_events.push_front(LocalEvent::Jump(
             data.entity,
-            data.body.jump_impulse().unwrap(),
+            data.body.jump_impulse().unwrap() * strength,
         ));
+        true
+    } else {
+        false
     }
 }
 
@@ -546,7 +549,9 @@ pub fn handle_input(data: &JoinData, update: &mut StateUpdate, input: InputKind)
             handle_ability(data, update, input)
         },
         InputKind::Roll => handle_dodge_input(data, update),
-        InputKind::Jump => handle_jump(data, update),
+        InputKind::Jump => {
+            handle_jump(data, update, 1.0);
+        },
         InputKind::Fly => {},
     }
 }
diff --git a/voxygen/src/audio/sfx/event_mapper/movement/tests.rs b/voxygen/src/audio/sfx/event_mapper/movement/tests.rs
index 018deaaa0c..67e2cfd4af 100644
--- a/voxygen/src/audio/sfx/event_mapper/movement/tests.rs
+++ b/voxygen/src/audio/sfx/event_mapper/movement/tests.rs
@@ -2,7 +2,8 @@ use super::*;
 use crate::audio::sfx::SfxEvent;
 use common::{
     comp::{
-        bird_small, humanoid, quadruped_medium, quadruped_small, Body, CharacterState, PhysicsState,
+        bird_small, humanoid, quadruped_medium, quadruped_small, Body, CharacterState, InputKind,
+        PhysicsState,
     },
     states,
     terrain::BlockKind,
@@ -184,6 +185,7 @@ fn maps_roll() {
                 recover_duration: Duration::default(),
                 roll_strength: 0.0,
                 immune_melee: false,
+                ability_info: empty_ability_info(),
             },
             timer: Duration::default(),
             stage_section: states::utils::StageSection::Buildup,
@@ -345,3 +347,12 @@ fn determines_relative_volumes() {
     assert!(quadruped_small < quadruped_medium);
     assert!(bird_small < quadruped_small);
 }
+
+fn empty_ability_info() -> states::utils::AbilityInfo {
+    states::utils::AbilityInfo {
+        tool: None,
+        hand: None,
+        input: InputKind::Primary,
+        select_pos: None,
+    }
+}