diff --git a/client/src/lib.rs b/client/src/lib.rs
index 51cc93fd67..5354b8f47a 100644
--- a/client/src/lib.rs
+++ b/client/src/lib.rs
@@ -248,6 +248,7 @@ pub struct Client {
     tick: u64,
     state: State,
 
+    server_view_distance_limit: Option<u32>,
     view_distance: Option<u32>,
     lod_distance: f32,
     // TODO: move into voxygen
@@ -707,6 +708,7 @@ impl Client {
 
             tick: 0,
             state,
+            server_view_distance_limit: None,
             view_distance: None,
             lod_distance: 4.0,
             loaded_distance: 0.0,
@@ -943,9 +945,11 @@ impl Client {
     }
 
     pub fn set_view_distance(&mut self, view_distance: u32) {
-        let view_distance = view_distance.max(1).min(65);
-        self.view_distance = Some(view_distance);
-        self.send_msg(ClientGeneral::SetViewDistance(view_distance));
+        if self.server_view_distance_limit.map_or(true, |limit| view_distance >= limit) {
+            let view_distance = view_distance.max(1).min(65);
+            self.view_distance = Some(view_distance);
+            self.send_msg(ClientGeneral::SetViewDistance(view_distance));
+        }
     }
 
     pub fn set_lod_distance(&mut self, lod_distance: u32) {
@@ -1486,6 +1490,8 @@ impl Client {
 
     pub fn view_distance(&self) -> Option<u32> { self.view_distance }
 
+    pub fn server_view_distance_limit(&self) -> Option<u32> { self.server_view_distance_limit }
+
     pub fn loaded_distance(&self) -> f32 { self.loaded_distance }
 
     pub fn position(&self) -> Option<Vec3<f32>> {
@@ -2254,6 +2260,9 @@ impl Client {
             ServerGeneral::SetViewDistance(vd) => {
                 self.view_distance = Some(vd);
                 frontend_events.push(Event::SetViewDistance(vd));
+                // If the server is correcting client vd selection we assume this is the max
+                // allowed view distance.
+                self.server_view_distance_limit = Some(vd);
             },
             ServerGeneral::Outcomes(outcomes) => {
                 frontend_events.extend(outcomes.into_iter().map(Event::Outcome))
diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs
index 8acb48ce9c..5d950626d1 100644
--- a/voxygen/src/hud/mod.rs
+++ b/voxygen/src/hud/mod.rs
@@ -3063,6 +3063,7 @@ impl Hud {
                 &self.imgs,
                 &self.fonts,
                 i18n,
+                client.server_view_distance_limit(),
                 fps as f32,
             )
             .set(self.ids.settings_window, ui_widgets)
diff --git a/voxygen/src/hud/settings_window/mod.rs b/voxygen/src/hud/settings_window/mod.rs
index 6b045ef933..6e55347e52 100644
--- a/voxygen/src/hud/settings_window/mod.rs
+++ b/voxygen/src/hud/settings_window/mod.rs
@@ -93,6 +93,7 @@ pub struct SettingsWindow<'a> {
     imgs: &'a Imgs,
     fonts: &'a Fonts,
     localized_strings: &'a Localization,
+    server_view_distance_limit: Option<u32>,
     fps: f32,
     #[conrod(common_builder)]
     common: widget::CommonBuilder,
@@ -105,6 +106,7 @@ impl<'a> SettingsWindow<'a> {
         imgs: &'a Imgs,
         fonts: &'a Fonts,
         localized_strings: &'a Localization,
+        server_view_distance_limit: Option<u32>,
         fps: f32,
     ) -> Self {
         Self {
@@ -113,6 +115,7 @@ impl<'a> SettingsWindow<'a> {
             imgs,
             fonts,
             localized_strings,
+            server_view_distance_limit,
             fps,
             common: widget::CommonBuilder::default(),
         }
@@ -299,11 +302,17 @@ impl<'a> Widget for SettingsWindow<'a> {
                 }
             },
             SettingsTab::Video => {
-                for change in
-                    video::Video::new(global_state, imgs, fonts, localized_strings, self.fps)
-                        .top_left_with_margins_on(state.ids.settings_content_align, 0.0, 0.0)
-                        .wh_of(state.ids.settings_content_align)
-                        .set(state.ids.video, ui)
+                for change in video::Video::new(
+                    global_state,
+                    imgs,
+                    fonts,
+                    localized_strings,
+                    self.server_view_distance_limit,
+                    self.fps,
+                )
+                .top_left_with_margins_on(state.ids.settings_content_align, 0.0, 0.0)
+                .wh_of(state.ids.settings_content_align)
+                .set(state.ids.video, ui)
                 {
                     events.push(Event::SettingsChange(change.into()));
                 }
@@ -327,11 +336,16 @@ impl<'a> Widget for SettingsWindow<'a> {
                 }
             },
             SettingsTab::Networking => {
-                for change in
-                    networking::Networking::new(global_state, imgs, fonts, localized_strings)
-                        .top_left_with_margins_on(state.ids.settings_content_align, 0.0, 0.0)
-                        .wh_of(state.ids.settings_content_align)
-                        .set(state.ids.networking, ui)
+                for change in networking::Networking::new(
+                    global_state,
+                    imgs,
+                    fonts,
+                    localized_strings,
+                    self.server_view_distance_limit,
+                )
+                .top_left_with_margins_on(state.ids.settings_content_align, 0.0, 0.0)
+                .wh_of(state.ids.settings_content_align)
+                .set(state.ids.networking, ui)
                 {
                     events.push(Event::SettingsChange(change.into()));
                 }
diff --git a/voxygen/src/hud/settings_window/networking.rs b/voxygen/src/hud/settings_window/networking.rs
index 8a7904e55e..64ecd4685c 100644
--- a/voxygen/src/hud/settings_window/networking.rs
+++ b/voxygen/src/hud/settings_window/networking.rs
@@ -34,6 +34,7 @@ pub struct Networking<'a> {
     imgs: &'a Imgs,
     fonts: &'a Fonts,
     localized_strings: &'a Localization,
+    server_view_distance_limit: Option<u32>,
     #[conrod(common_builder)]
     common: widget::CommonBuilder,
 }
@@ -43,12 +44,14 @@ impl<'a> Networking<'a> {
         imgs: &'a Imgs,
         fonts: &'a Fonts,
         localized_strings: &'a Localization,
+        server_view_distance_limit: Option<u32>,
     ) -> Self {
         Self {
             global_state,
             imgs,
             fonts,
             localized_strings,
+            server_view_distance_limit,
             common: widget::CommonBuilder::default(),
         }
     }
@@ -107,6 +110,7 @@ impl<'a> Widget for Networking<'a> {
         .down_from(state.ids.vd_text, 8.0)
         .track_breadth(12.0)
         .slider_length(10.0)
+        .soft_max(self.server_view_distance_limit.unwrap_or(u32::MAX))
         .pad_track((5.0, 5.0))
         .set(state.ids.vd_slider, ui)
         {
diff --git a/voxygen/src/hud/settings_window/video.rs b/voxygen/src/hud/settings_window/video.rs
index 2d4d9cd1a8..4e56a2dc41 100644
--- a/voxygen/src/hud/settings_window/video.rs
+++ b/voxygen/src/hud/settings_window/video.rs
@@ -131,6 +131,7 @@ pub struct Video<'a> {
     imgs: &'a Imgs,
     fonts: &'a Fonts,
     localized_strings: &'a Localization,
+    server_view_distance_limit: Option<u32>,
     fps: f32,
     #[conrod(common_builder)]
     common: widget::CommonBuilder,
@@ -141,6 +142,7 @@ impl<'a> Video<'a> {
         imgs: &'a Imgs,
         fonts: &'a Fonts,
         localized_strings: &'a Localization,
+        server_view_distance_limit: Option<u32>,
         fps: f32,
     ) -> Self {
         Self {
@@ -148,6 +150,7 @@ impl<'a> Video<'a> {
             imgs,
             fonts,
             localized_strings,
+            server_view_distance_limit,
             fps,
             common: widget::CommonBuilder::default(),
         }
@@ -295,6 +298,7 @@ impl<'a> Widget for Video<'a> {
         .down_from(state.ids.vd_text, 8.0)
         .track_breadth(12.0)
         .slider_length(10.0)
+        .soft_max(self.server_view_distance_limit.unwrap_or(u32::MAX))
         .pad_track((5.0, 5.0))
         .set(state.ids.vd_slider, ui)
         {
diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs
index 3fbbf7e212..720e365ebe 100644
--- a/voxygen/src/menu/char_selection/mod.rs
+++ b/voxygen/src/menu/char_selection/mod.rs
@@ -215,12 +215,7 @@ impl PlayState for CharSelectionState {
                 Ok(events) => {
                     for event in events {
                         match event {
-                            client::Event::SetViewDistance(vd) => {
-                                global_state.settings.graphics.view_distance = vd;
-                                global_state
-                                    .settings
-                                    .save_to_file_warn(&global_state.config_dir);
-                            },
+                            client::Event::SetViewDistance(_vd) => {},
                             client::Event::Disconnect => {
                                 global_state.info_message = Some(
                                     localized_strings
diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs
index 88246035b1..7b7d3cfa1e 100644
--- a/voxygen/src/menu/main/mod.rs
+++ b/voxygen/src/menu/main/mod.rs
@@ -231,12 +231,7 @@ impl PlayState for MainMenuState {
                 Ok(events) => {
                     for event in events {
                         match event {
-                            client::Event::SetViewDistance(vd) => {
-                                global_state.settings.graphics.view_distance = vd;
-                                global_state
-                                    .settings
-                                    .save_to_file_warn(&global_state.config_dir);
-                            },
+                            client::Event::SetViewDistance(_vd) => {},
                             client::Event::Disconnect => {
                                 global_state.info_message = Some(
                                     localized_strings
diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs
index f3ab46bdff..4cae6af2b1 100644
--- a/voxygen/src/session/mod.rs
+++ b/voxygen/src/session/mod.rs
@@ -351,12 +351,7 @@ impl SessionState {
                 client::Event::Notification(n) => {
                     self.hud.new_notification(n);
                 },
-                client::Event::SetViewDistance(vd) => {
-                    global_state.settings.graphics.view_distance = vd;
-                    global_state
-                        .settings
-                        .save_to_file_warn(&global_state.config_dir);
-                },
+                client::Event::SetViewDistance(_vd) => {},
                 client::Event::Outcome(outcome) => outcomes.push(outcome),
                 client::Event::CharacterCreated(_) => {},
                 client::Event::CharacterEdited(_) => {},
diff --git a/voxygen/src/session/settings_change.rs b/voxygen/src/session/settings_change.rs
index 4870820688..f53d4738c9 100644
--- a/voxygen/src/session/settings_change.rs
+++ b/voxygen/src/session/settings_change.rs
@@ -355,12 +355,7 @@ impl SettingsChange {
             SettingsChange::Graphics(graphics_change) => {
                 match graphics_change {
                     Graphics::AdjustViewDistance(view_distance) => {
-                        session_state
-                            .client
-                            .borrow_mut()
-                            .set_view_distance(view_distance);
-
-                        settings.graphics.view_distance = view_distance;
+                        adjust_view_distance(view_distance, global_state, session_state)
                     },
                     Graphics::AdjustLodDistance(lod_distance) => {
                         session_state
@@ -605,11 +600,7 @@ impl SettingsChange {
             },
             SettingsChange::Networking(networking_change) => match networking_change {
                 Networking::AdjustViewDistance(view_distance) => {
-                    session_state
-                        .client
-                        .borrow_mut()
-                        .set_view_distance(view_distance);
-                    settings.graphics.view_distance = view_distance;
+                    adjust_view_distance(view_distance, global_state, session_state)
                 },
                 Networking::ChangePlayerPhysicsBehavior {
                     server_authoritative,
@@ -654,6 +645,21 @@ impl SettingsChange {
                 },
             },
         }
-        settings.save_to_file_warn(&global_state.config_dir);
+        global_state
+            .settings
+            .save_to_file_warn(&global_state.config_dir);
     }
 }
+
+fn adjust_view_distance(
+    view_distance: u32,
+    global_state: &mut GlobalState,
+    session_state: &mut SessionState,
+) {
+    session_state
+        .client
+        .borrow_mut()
+        .set_view_distance(view_distance);
+
+    global_state.settings.graphics.view_distance = view_distance;
+}
diff --git a/voxygen/src/ui/widgets/image_slider.rs b/voxygen/src/ui/widgets/image_slider.rs
index dbff4e518e..cc273b8f15 100644
--- a/voxygen/src/ui/widgets/image_slider.rs
+++ b/voxygen/src/ui/widgets/image_slider.rs
@@ -29,6 +29,10 @@ pub struct ImageSlider<T, K> {
     value: T,
     min: T,
     max: T,
+    // If `value > soft_max` we will display the slider at `soft_max` along with a faded ghost
+    // slider at `value`. The slider displayed at `soft_max` is purely a visual indicator and has
+    // no effect on the values produced by this slider.
+    soft_max: T,
     /// The amount in which the slider's display should be skewed.
     ///
     /// Higher skew amounts (above 1.0) will weigh lower values.
@@ -65,6 +69,7 @@ widget_ids! {
     struct Ids {
         track,
         slider,
+        soft_max_slider,
     }
 }
 
@@ -76,6 +81,7 @@ pub struct State {
 impl<T, K> ImageSlider<T, K> {
     builder_methods! {
         pub skew { skew = f32 }
+        pub soft_max { soft_max = T }
         pub pad_track { track.padding = (f32, f32) }
         pub hover_image { slider.hover_image_id = Some(image::Id) }
         pub press_image { slider.press_image_id = Some(image::Id) }
@@ -85,18 +91,16 @@ impl<T, K> ImageSlider<T, K> {
         pub slider_color { slider.color = Some(Color) }
     }
 
-    fn new(
-        value: T,
-        min: T,
-        max: T,
-        slider_image_id: image::Id,
-        track_image_id: image::Id,
-    ) -> Self {
+    fn new(value: T, min: T, max: T, slider_image_id: image::Id, track_image_id: image::Id) -> Self
+    where
+        T: Copy,
+    {
         Self {
             common: widget::CommonBuilder::default(),
             value,
             min,
             max,
+            soft_max: max,
             skew: 1.0,
             track: Track {
                 image_id: track_image_id,
@@ -133,7 +137,7 @@ where
 
 impl<T> ImageSlider<T, Discrete>
 where
-    T: Integer,
+    T: Integer + Copy,
 {
     pub fn discrete(
         value: T,
@@ -266,45 +270,67 @@ where
             .unwrap_or(slider.image_id);
 
         // A rectangle for positioning and sizing the slider.
-        let value_perc = utils::map_range(new_value, min, max, 0.0, 1.0);
-        let unskewed_perc = value_perc.powf(1.0 / skew as f64);
-        let slider_rect = if is_horizontal {
-            let pos = utils::map_range(
-                unskewed_perc,
-                0.0,
-                1.0,
-                rect.x.start + start_pad,
-                rect.x.end - end_pad,
-            );
-            let w = slider.length.map_or(rect.w() / 10.0, |w| w as f64);
-            Rect {
-                x: Range::from_pos_and_len(pos, w),
-                ..rect
-            }
-        } else {
-            let pos = utils::map_range(
-                unskewed_perc,
-                0.0,
-                1.0,
-                rect.y.start + start_pad,
-                rect.y.end - end_pad,
-            );
-            let h = slider.length.map_or(rect.h() / 10.0, |h| h as f64);
-            Rect {
-                y: Range::from_pos_and_len(pos, h),
-                ..rect
+        let slider_rect = |slider_value| {
+            let value_perc = utils::map_range(slider_value, min, max, 0.0, 1.0);
+            let unskewed_perc = value_perc.powf(1.0 / skew as f64);
+            if is_horizontal {
+                let pos = utils::map_range(
+                    unskewed_perc,
+                    0.0,
+                    1.0,
+                    rect.x.start + start_pad,
+                    rect.x.end - end_pad,
+                );
+                let w = slider.length.map_or(rect.w() / 10.0, |w| w as f64);
+                Rect {
+                    x: Range::from_pos_and_len(pos, w),
+                    ..rect
+                }
+            } else {
+                let pos = utils::map_range(
+                    unskewed_perc,
+                    0.0,
+                    1.0,
+                    rect.y.start + start_pad,
+                    rect.y.end - end_pad,
+                );
+                let h = slider.length.map_or(rect.h() / 10.0, |h| h as f64);
+                Rect {
+                    y: Range::from_pos_and_len(pos, h),
+                    ..rect
+                }
             }
         };
 
-        let (x, y, w, h) = slider_rect.x_y_w_h();
+        // Whether soft max slider needs to be displayed and main slider faded to look
+        // like a ghost.
+        let over_soft_max = new_value > self.soft_max;
+
+        let (x, y, w, h) = slider_rect(new_value).x_y_w_h();
+        let fade = if over_soft_max { 0.5 } else { 1.0 };
         Image::new(slider_image)
             .x_y(x, y)
             .w_h(w, h)
             .parent(id)
             .graphics_for(id)
-            .color(slider.color)
+            .color(Some(
+                slider
+                    .color
+                    .map_or(Color::Rgba(1.0, 1.0, 1.0, fade), |c: Color| c.alpha(fade)),
+            ))
             .set(state.ids.slider, ui);
 
+        if over_soft_max {
+            let (x, y, w, h) = slider_rect(self.soft_max).x_y_w_h();
+            Image::new(slider_image)
+                .x_y(x, y)
+                .w_h(w, h)
+                .parent(id)
+                .graphics_for(id)
+                .color(slider.color)
+                .set(state.ids.soft_max_slider, ui);
+        }
+
         // If the value has just changed, return the new value.
         if value != new_value {
             Some(new_value)