diff --git a/crates/paint/src/brush_tests.rs b/crates/paint/src/brush_tests.rs
--- a/crates/paint/src/brush_tests.rs
+++ b/crates/paint/src/brush_tests.rs
@@ -246,6 +246,43 @@ fn pencil_1px_line_is_the_same_whichever_way_and_however_smoothed() {
     assert_eq!(sampled, pixels((1.5, 1.5), (20.5, 9.5), 0.0));
 }
 
+#[test]
+fn pencil_1px_freehand_line_never_breaks_where_it_turns() {
+    // #8040: each segment of a freehand stroke samples its own line, so where the stroke turned
+    // the last pixel of one segment and the first of the next could sit two pixels apart.
+    let b = BrushSettings { aliased: true, hardness: 1.0, size: 1.0, ..brush() };
+    let mut seed = 0x2545_f491_4f6c_dd1du64;
+    let mut rnd = || {
+        seed ^= seed << 13;
+        seed ^= seed >> 7;
+        seed ^= seed << 17;
+        (seed >> 11) as f64 / (1u64 << 53) as f64
+    };
+    for _ in 0..300 {
+        let mut pts = vec![StrokePoint::new(20.0 + rnd() * 8.0, 20.0 + rnd() * 8.0, 1.0)];
+        for _ in 0..6 {
+            let l = *pts.last().unwrap();
+            let (dx, dy) = ((rnd() - 0.5) * 6.0, (rnd() - 0.5) * 6.0);
+            pts.push(StrokePoint::new((l.x + dx).clamp(1.0, 46.0), (l.y + dy).clamp(1.0, 46.0), 1.0));
+        }
+        let s = paint(&b, &pts, 48, 48);
+        let painted: Vec<(i32, i32)> = (0..48).flat_map(|y| (0..48).map(move |x| (x, y))).filter(|&(x, y)| s.rgba(x, y)[3] > 0.0).collect();
+        // Every painted pixel is reachable from the first through 8-connected neighbours.
+        let mut reached = vec![painted[0]];
+        let mut i = 0;
+        while i < reached.len() {
+            let (x, y) = reached[i];
+            for &q in &painted {
+                if (q.0 - x).abs() <= 1 && (q.1 - y).abs() <= 1 && !reached.contains(&q) {
+                    reached.push(q);
+                }
+            }
+            i += 1;
+        }
+        let path: Vec<(f64, f64)> = pts.iter().map(|p| (p.x, p.y)).collect();
+        assert_eq!(reached.len(), painted.len(), "the line breaks along {path:?}");
+    }
+}
 // ---------- spacing ----------
 
 #[test]
diff --git a/crates/paint/src/dynamics.rs b/crates/paint/src/dynamics.rs
--- a/crates/paint/src/dynamics.rs
+++ b/crates/paint/src/dynamics.rs
@@ -21,6 +21,8 @@ pub const SPEED_SPACING_MS: f64 = 8.0;
 
 /// Longest pixel-grid segment (in pixels) before falling back to distance spacing.
 const MAX_GRID_STEPS: f64 = 1e7;
+/// Widest gap (in pixels) [`PathWalker::bridge`] fills between two pixel-grid segments.
+const MAX_BRIDGE: f64 = 4.0;
 
 /// Densest speed spacing, in pixels between dabs (a slow, long stroke never floods the buffer).
 const MIN_SPEED_STEP: f64 = 0.5;
@@ -242,6 +244,8 @@ pub struct PathWalker {
     /// Pixel-grid stepping for small aliased tips: the dab-centre offset inside a pixel (see
     /// [`PathWalker::pixel_grid`]).
     grid: Option<f64>,
+    /// The grid cell of the last pixel-grid step, so the next segment joins it (#8040).
+    cell: Option<(f64, f64)>,
 }
 
 impl PathWalker {
@@ -258,6 +262,7 @@ impl PathWalker {
             speed: None,
             speed_acc: 0.0,
             grid: None,
+            cell: None,
         }
     }
 
@@ -296,11 +301,32 @@ impl PathWalker {
             let mut q = lerp_pt(a, p, t.clamp(0.0, 1.0));
             let cn = grid_cell(an + dn * t, offset);
             (q.x, q.y) = if steep { (cn, c) } else { (c, cn) };
+            self.bridge(&q, out);
             self.emit(q, out);
         }
         true
     }
 
+    /// Fills the pixels between the last grid step and `q` when they don't touch. Each segment
+    /// samples its own line, so where a freehand stroke turns, the last pixel of one segment and
+    /// the first of the next can sit two pixels apart, and the 1 px Pencil line breaks (#8040).
+    fn bridge(&mut self, q: &StrokePoint, out: &mut impl FnMut(StepInput)) {
+        let Some((mut x, mut y)) = self.cell else { return };
+        // Only a turn's small gap: a step cut off by the walker's budget is not filled in.
+        if (q.x - x).abs().max((q.y - y).abs()) > MAX_BRIDGE {
+            return;
+        }
+        while (q.x - x).abs() > 1.5 || (q.y - y).abs() > 1.5 {
+            if (q.x - x).abs() > 0.5 {
+                x += (q.x - x).signum();
+            }
+            if (q.y - y).abs() > 0.5 {
+                y += (q.y - y).signum();
+            }
+            self.emit(StrokePoint { x, y, ..*q }, out);
+        }
+    }
+
     /// Space steps by stroke time instead of distance ([`SPEED_SPACING_MS`]): faster movement
     /// spreads them out. Segments without timestamps are spaced by distance.
     pub fn speed_spacing(mut self, interval_ms: f64) -> Self {
@@ -309,6 +335,9 @@ impl PathWalker {
     }
 
     fn emit(&mut self, point: StrokePoint, out: &mut impl FnMut(StepInput)) {
+        if let Some(g) = self.grid {
+            self.cell = Some((grid_cell(point.x, g), grid_cell(point.y, g)));
+        }
         let s = StepInput { point, direction: self.dir, initial_direction: self.initial_dir.unwrap_or(self.dir), step: self.step };
         self.step += 1;
         out(s);
