diff --git a/client/src/lib.rs b/client/src/lib.rs
index 0faac1c290..c043cc0df9 100644
--- a/client/src/lib.rs
+++ b/client/src/lib.rs
@@ -45,6 +45,7 @@ pub struct Client {
     tick: u64,
     state: State,
     entity: EcsEntity,
+
     view_distance: Option<u32>,
     loaded_distance: Option<u32>,
 
@@ -230,9 +231,9 @@ impl Client {
             let mut chunks_to_remove = Vec::new();
             self.state.terrain().iter().for_each(|(key, _)| {
                 if (Vec2::from(chunk_pos) - Vec2::from(key))
-                    .map(|e: i32| e.abs() as u32)
-                    .reduce_max()
-                    > view_distance + 1
+                    .map(|e: i32| (e.abs() as u32).checked_sub(2).unwrap_or(0))
+                    .magnitude_squared()
+                    > view_distance.pow(2)
                 {
                     chunks_to_remove.push(key);
                 }
@@ -242,18 +243,39 @@ impl Client {
             }
 
             // Request chunks from the server.
-            // TODO: This is really inefficient.
             let mut all_loaded = true;
             'outer: for dist in 0..=view_distance as i32 {
-                for i in chunk_pos.x - dist..=chunk_pos.x + 1 + dist {
-                    for j in chunk_pos.y - dist..=chunk_pos.y + 1 + dist {
-                        let key = Vec2::new(i, j);
-                        if self.state.terrain().get_key(key).is_none() {
-                            if !self.pending_chunks.contains_key(&key) {
+                // Only iterate through chunks that need to be loaded for circular vd
+                // The (dist - 2) explained:
+                // -0.5 because a chunk is visible if its corner is within the view distance
+                // -0.5 for being able to move to the corner of the current chunk
+                // -1 because chunks are not meshed if they don't have all their neighbors
+                //     (notice also that view_distance is decreased by 1)
+                //     (this subtraction on vd is ommitted elsewhere in order to provide a buffer layer of loaded chunks)
+                let top = if 2 * (dist - 2).max(0).pow(2) > (view_distance - 1).pow(2) as i32 {
+                    ((view_distance - 1).pow(2) as f32 - (dist - 2).pow(2) as f32)
+                        .sqrt()
+                        .round() as i32
+                        + 1
+                } else {
+                    dist
+                };
+
+                for i in -top..=top {
+                    let keys = [
+                        chunk_pos + Vec2::new(dist, i),
+                        chunk_pos + Vec2::new(i, dist),
+                        chunk_pos + Vec2::new(-dist, i),
+                        chunk_pos + Vec2::new(i, -dist),
+                    ];
+
+                    for key in keys.iter() {
+                        if self.state.terrain().get_key(*key).is_none() {
+                            if !self.pending_chunks.contains_key(key) {
                                 if self.pending_chunks.len() < 4 {
                                     self.postbox
-                                        .send_message(ClientMsg::TerrainChunkRequest { key });
-                                    self.pending_chunks.insert(key, Instant::now());
+                                        .send_message(ClientMsg::TerrainChunkRequest { key: *key });
+                                    self.pending_chunks.insert(*key, Instant::now());
                                 } else {
                                     break 'outer;
                                 }
diff --git a/server/src/lib.rs b/server/src/lib.rs
index 79a382feae..747ef7f272 100644
--- a/server/src/lib.rs
+++ b/server/src/lib.rs
@@ -295,11 +295,11 @@ impl Server {
                 })
             {
                 let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32));
-                let dist = (Vec2::from(chunk_pos) - Vec2::from(key))
-                    .map(|e: i32| e.abs())
-                    .reduce_max() as u32;
+                let adjusted_dist_sqr = (Vec2::from(chunk_pos) - Vec2::from(key))
+                    .map(|e: i32| (e.abs() as u32).checked_sub(2).unwrap_or(0))
+                    .magnitude_squared();
 
-                if dist <= view_distance + 1 {
+                if adjusted_dist_sqr <= view_distance.pow(2) {
                     self.clients.notify(
                         entity,
                         ServerMsg::TerrainChunkUpdate {
@@ -327,13 +327,14 @@ impl Server {
                 .join()
             {
                 let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32));
-                let dist = Vec2::from(chunk_pos - key)
-                    .map(|e: i32| e.abs() as u32)
-                    .reduce_max();
+
+                let adjusted_dist_sqr = Vec2::from(chunk_pos - key)
+                    .map(|e: i32| (e.abs() as u32).checked_sub(2).unwrap_or(0))
+                    .magnitude_squared();
 
                 if player
                     .view_distance
-                    .map(|vd| dist <= vd + 1)
+                    .map(|vd| adjusted_dist_sqr <= vd.pow(2))
                     .unwrap_or(false)
                 {
                     should_drop = false;
@@ -696,9 +697,10 @@ impl Server {
 
                 (pos.0 - client_pos)
                     .map2(TerrainChunkSize::SIZE, |d, sz| {
-                        (d.abs() as u32) < client_vd * sz as u32
+                        (d.abs() as u32 / sz).checked_sub(2).unwrap_or(0)
                     })
-                    .reduce_and()
+                    .magnitude_squared()
+                    < client_vd.pow(2)
             };
 
             match force_update {
diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs
index 34ecf90771..9dfaf90800 100644
--- a/voxygen/src/hud/mod.rs
+++ b/voxygen/src/hud/mod.rs
@@ -55,11 +55,13 @@ widget_ids! {
 
         // Test
         bag_space_add,
+
         // Debug
         debug_bg,
         fps_counter,
         ping,
         coordinates,
+        loaded_distance,
 
         // Game Version
         version,
@@ -336,10 +338,9 @@ impl Hud {
                 // Don't process nametags outside the vd (visibility further limited by ui backend)
                 .filter(|(_, pos, _, _, _)| {
                     (pos.0 - player_pos)
-                        .map2(TerrainChunkSize::SIZE, |d, sz| {
-                            (d.abs() as u32) < view_distance * sz as u32
-                        })
-                        .reduce_and()
+                        .map2(TerrainChunkSize::SIZE, |d, sz| d.abs() as f32 / sz as f32)
+                        .magnitude()
+                        < view_distance as f32
                 })
                 .map(|(_, pos, actor, _, player)| match actor {
                     comp::Actor::Character {
@@ -380,10 +381,9 @@ impl Hud {
                 // Don't process health bars outside the vd (visibility further limited by ui backend)
                 .filter(|(_, pos, _)| {
                     (pos.0 - player_pos)
-                        .map2(TerrainChunkSize::SIZE, |d, sz| {
-                            (d.abs() as u32) < view_distance * sz as u32
-                        })
-                        .reduce_and()
+                        .map2(TerrainChunkSize::SIZE, |d, sz| d.abs() as f32 / sz as f32)
+                        .magnitude()
+                        < view_distance as f32
                 })
             {
                 let back_id = health_back_id_walker.next(
@@ -425,18 +425,21 @@ impl Hud {
                 .font_id(self.fonts.opensans)
                 .color(TEXT_COLOR)
                 .set(self.ids.version, ui_widgets);
+            // Ticks per second
             Text::new(&format!("FPS: {:.1}", debug_info.tps))
                 .color(TEXT_COLOR)
                 .down_from(self.ids.version, 5.0)
                 .font_id(self.fonts.opensans)
                 .font_size(14)
                 .set(self.ids.fps_counter, ui_widgets);
+            // Ping
             Text::new(&format!("Ping: {:.1}ms", debug_info.ping_ms))
                 .color(TEXT_COLOR)
                 .down_from(self.ids.fps_counter, 5.0)
                 .font_id(self.fonts.opensans)
                 .font_size(14)
                 .set(self.ids.ping, ui_widgets);
+            // Players position
             let coordinates_text = match debug_info.coordinates {
                 Some(coordinates) => format!("Coordinates: {:.1}", coordinates.0),
                 None => "Player has no Pos component".to_owned(),
@@ -447,6 +450,16 @@ impl Hud {
                 .font_id(self.fonts.opensans)
                 .font_size(14)
                 .set(self.ids.coordinates, ui_widgets);
+            // Loaded distance
+            Text::new(&format!(
+                "View distance: {} chunks",
+                client.loaded_distance().unwrap_or(0)
+            ))
+            .color(TEXT_COLOR)
+            .down_from(self.ids.coordinates, 5.0)
+            .font_id(self.fonts.opensans)
+            .font_size(14)
+            .set(self.ids.loaded_distance, ui_widgets);
         }
 
         // Add Bag-Space Button.
diff --git a/voxygen/src/scene/figure.rs b/voxygen/src/scene/figure.rs
index 8d07489fa6..1e4c393f7b 100644
--- a/voxygen/src/scene/figure.rs
+++ b/voxygen/src/scene/figure.rs
@@ -493,13 +493,12 @@ impl FigureMgr {
             .join()
         {
             // Don't process figures outside the vd
-            let vd_percent = (pos.0 - player_pos)
-                .map2(TerrainChunkSize::SIZE, |d, sz| {
-                    (100 * d.abs() as u32) / (view_distance * sz)
-                })
-                .reduce_max();
+            let vd_frac = (pos.0 - player_pos)
+                .map2(TerrainChunkSize::SIZE, |d, sz| d.abs() as f32 / sz as f32)
+                .magnitude()
+                / view_distance as f32;
             // Keep from re-adding/removing entities on the border of the vd
-            if vd_percent > 120 {
+            if vd_frac > 1.2 {
                 match actor {
                     comp::Actor::Character { body, .. } => match body {
                         Body::Humanoid(_) => {
@@ -514,7 +513,7 @@ impl FigureMgr {
                     },
                 }
                 continue;
-            } else if vd_percent > 100 {
+            } else if vd_frac > 1.0 {
                 continue;
             }
 
@@ -693,10 +692,9 @@ impl FigureMgr {
             // Don't render figures outside the vd
             .filter(|(_, pos, _, _, _, _, _)| {
                 (pos.0 - player_pos)
-                    .map2(TerrainChunkSize::SIZE, |d, sz| {
-                        (d.abs() as u32) < view_distance * sz as u32
-                    })
-                    .reduce_and()
+                    .map2(TerrainChunkSize::SIZE, |d, sz| d.abs() as f32 / sz as f32)
+                    .magnitude()
+                    < view_distance as f32
             })
             // Don't render dead entities
             .filter(|(_, _, _, _, _, _, stats)| stats.map_or(true, |s| !s.is_dead))