add unscaled strokes
diff --git a/examples/scenes/src/test_scenes.rs b/examples/scenes/src/test_scenes.rs
index 23ea373..d3aa9d4 100644
--- a/examples/scenes/src/test_scenes.rs
+++ b/examples/scenes/src/test_scenes.rs
@@ -82,6 +82,13 @@
         None,
         &only_movetos,
     );
+    sb.fill(
+        Fill::NonZero,
+        Affine::translate((100.0, 100.0)),
+        Color::rgb8(0, 0, 255),
+        None,
+        &missing_movetos,
+    );
     sb.stroke(
         &Stroke::new(8.0),
         Affine::translate((100.0, 100.0)),
@@ -89,6 +96,20 @@
         None,
         &missing_movetos,
     );
+    sb.fill(
+        Fill::NonZero,
+        Affine::translate((300.0, 100.0)),
+        Color::rgb8(0, 0, 255),
+        None,
+        &missing_movetos,
+    );
+    sb.stroke(
+        &Stroke::new(8.0).with_scale(false),
+        Affine::translate((300.0, 100.0)),
+        Color::rgb8(0, 255, 255),
+        None,
+        &missing_movetos,
+    );
 }
 
 fn cardioid_and_friends(sb: &mut SceneBuilder, _: &mut SceneParams) {
diff --git a/shader/coarse.wgsl b/shader/coarse.wgsl
index 58fe409..c5e61b8 100644
--- a/shader/coarse.wgsl
+++ b/shader/coarse.wgsl
@@ -82,11 +82,22 @@
     }
 }
 
-fn write_path(tile: Tile, linewidth: f32) -> bool {
+fn is_stroke(linewidth: u32) -> bool {
+    return (linewidth & 0x7f800000u) != 0x7f800000u;
+}
+
+fn write_path(tile: Tile, linewidth: u32) -> bool {
     // TODO: take flags
     alloc_cmd(3u);
-    if linewidth < 0.0 {
-        let even_odd = linewidth < -1.0;
+    let linewidth_f32 = bitcast<f32>(linewidth);
+    if is_stroke(linewidth) {
+        let stroke = CmdStroke(tile.segments, 0.5 * linewidth_f32);
+        ptcl[cmd_offset] = CMD_STROKE;
+        ptcl[cmd_offset + 1u] = stroke.tile;
+        ptcl[cmd_offset + 2u] = bitcast<u32>(stroke.half_width);
+        cmd_offset += 3u;        
+    } else {
+        let even_odd = linewidth == 0x7f800000u;
         if tile.segments != 0u {
             let fill = CmdFill(tile.segments, tile.backdrop);
             ptcl[cmd_offset] = CMD_FILL;
@@ -101,12 +112,6 @@
             ptcl[cmd_offset] = CMD_SOLID;
             cmd_offset += 1u;
         }
-    } else {
-        let stroke = CmdStroke(tile.segments, 0.5 * linewidth);
-        ptcl[cmd_offset] = CMD_STROKE;
-        ptcl[cmd_offset + 1u] = stroke.tile;
-        ptcl[cmd_offset + 2u] = bitcast<u32>(stroke.half_width);
-        cmd_offset += 3u;
     }
     return true;
 }
@@ -360,7 +365,7 @@
                 switch drawtag {
                     // DRAWTAG_FILL_COLOR
                     case 0x44u: {
-                        let linewidth = bitcast<f32>(info_bin_data[di]);
+                        let linewidth = info_bin_data[di];
                         if write_path(tile, linewidth) {
                             let rgba_color = scene[dd];
                             write_color(CmdColor(rgba_color));
@@ -368,7 +373,7 @@
                     }
                     // DRAWTAG_FILL_LIN_GRADIENT
                     case 0x114u: {
-                        let linewidth = bitcast<f32>(info_bin_data[di]);
+                        let linewidth = info_bin_data[di];
                         if write_path(tile, linewidth) {
                             let index = scene[dd];
                             let info_offset = di + 1u;
@@ -377,7 +382,7 @@
                     }
                     // DRAWTAG_FILL_RAD_GRADIENT
                     case 0x2dcu: {
-                        let linewidth = bitcast<f32>(info_bin_data[di]);
+                        let linewidth = info_bin_data[di];
                         if write_path(tile, linewidth) {
                             let index = scene[dd];
                             let info_offset = di + 1u;
@@ -386,7 +391,7 @@
                     }
                     // DRAWTAG_FILL_IMAGE
                     case 0x248u: {
-                        let linewidth = bitcast<f32>(info_bin_data[di]);
+                        let linewidth = info_bin_data[di];
                         if write_path(tile, linewidth) {                            
                             write_image(di + 1u);
                         }
@@ -405,7 +410,7 @@
                     // DRAWTAG_END_CLIP
                     case 0x21u: {
                         clip_depth -= 1u;
-                        write_path(tile, -1.0);
+                        write_path(tile, 0x7fc00000u);
                         let blend = scene[dd];
                         let alpha = bitcast<f32>(scene[dd + 1u]);
                         write_end_clip(CmdEndClip(blend, alpha));
diff --git a/shader/draw_leaf.wgsl b/shader/draw_leaf.wgsl
index b149d27..5c7aa15 100644
--- a/shader/draw_leaf.wgsl
+++ b/shader/draw_leaf.wgsl
@@ -49,6 +49,10 @@
     return Transform(matrx, translate);
 }
 
+fn is_stroke(linewidth: u32) -> bool {
+    return (linewidth & 0x7f800000u) != 0x7f800000u;
+}
+
 var<workgroup> sh_scratch: array<DrawMonoid, WG_SIZE>;
 
 @compute @workgroup_size(256)
@@ -109,20 +113,26 @@
         // let x1 = f32(bbox.x1);
         // let y1 = f32(bbox.y1);
         // let bbox_f = vec4(x0, y0, x1, y1);
-        let fill_mode = u32(bbox.linewidth >= 0.0);
+        // let fill_mode = u32(bbox.linewidth >= 0.0);
         var matrx: vec4<f32>;
         var translate: vec2<f32>;
-        var linewidth = bbox.linewidth;
-        if linewidth >= 0.0 || tag_word == DRAWTAG_FILL_LIN_GRADIENT || tag_word == DRAWTAG_FILL_RAD_GRADIENT ||
+        let linewidth_u32 = bbox.linewidth;
+        var linewidth = bitcast<f32>(linewidth_u32);
+        let stroked = is_stroke(linewidth_u32);
+        if stroked || tag_word == DRAWTAG_FILL_LIN_GRADIENT || tag_word == DRAWTAG_FILL_RAD_GRADIENT ||
             tag_word == DRAWTAG_FILL_IMAGE 
         {
             let transform = read_transform(config.transform_base, bbox.trans_ix);
             matrx = transform.matrx;
             translate = transform.translate;
-        }
-        if linewidth >= 0.0 {
-            // Note: doesn't deal with anisotropic case
-            linewidth *= sqrt(abs(matrx.x * matrx.w - matrx.y * matrx.z));
+            if stroked {
+                if linewidth >= 0.0 {
+                    // Note: doesn't deal with anisotropic case
+                    linewidth *= sqrt(abs(matrx.x * matrx.w - matrx.y * matrx.z));
+                } else {
+                    linewidth = -linewidth;
+                }
+            }
         }
         switch tag_word {
             // DRAWTAG_FILL_COLOR
diff --git a/shader/pathseg.wgsl b/shader/pathseg.wgsl
index ec059ab..2ca59d5 100644
--- a/shader/pathseg.wgsl
+++ b/shader/pathseg.wgsl
@@ -29,7 +29,7 @@
     y0: atomic<i32>,
     x1: atomic<i32>,
     y1: atomic<i32>,
-    linewidth: f32,
+    linewidth: u32,
     trans_ix: u32,
 }
 
@@ -115,6 +115,10 @@
     return i32(ceil(x));
 }
 
+fn is_stroke(linewidth: u32) -> bool {
+    return (linewidth & 0x7f800000u) != 0x7f800000u;
+}
+
 @compute @workgroup_size(256)
 fn main(
     @builtin(global_invocation_id) global_id: vec3<u32>,
@@ -129,7 +133,7 @@
     var tag_byte = (tag_word >> shift) & 0xffu;
 
     let out = &path_bboxes[tm.path_ix];
-    let linewidth = bitcast<f32>(scene[config.linewidth_base + tm.linewidth_ix]);
+    let linewidth = scene[config.linewidth_base + tm.linewidth_ix];
     if (tag_byte & PATH_TAG_PATH) != 0u {
         (*out).linewidth = linewidth;
         (*out).trans_ix = tm.trans_ix;
@@ -182,14 +186,20 @@
             }
         }
         var stroke = vec2(0.0, 0.0);
-        if linewidth >= 0.0 {
-            // See https://www.iquilezles.org/www/articles/ellipses/ellipses.htm
-            // This is the correct bounding box, but we're not handling rendering
-            // in the isotropic case, so it may mismatch.
-            stroke = 0.5 * linewidth * vec2(length(transform.matrx.xz), length(transform.matrx.yw));
+        var flags = 0u;
+        if is_stroke(linewidth) {
+            let linewidth_f32 = bitcast<f32>(linewidth);
+            if linewidth_f32 >= 0.0 {
+                // See https://www.iquilezles.org/www/articles/ellipses/ellipses.htm
+                // This is the correct bounding box, but we're not handling rendering
+                // in the isotropic case, so it may mismatch.
+                stroke = 0.5 * linewidth_f32 * vec2(length(transform.matrx.xz), length(transform.matrx.yw));
+            } else {
+                stroke = 0.5 * linewidth_f32 * vec2(-1.0);
+            }
             bbox += vec4(-stroke, stroke);
+            flags = 1u;
         }
-        let flags = u32(linewidth >= 0.0);
         cubics[global_id.x] = Cubic(p0, p1, p2, p3, stroke, tm.path_ix, flags);
         // Update bounding box using atomics only. Computing a monoid is a
         // potential future optimization.
diff --git a/shader/shared/bbox.wgsl b/shader/shared/bbox.wgsl
index 714602d..52142b9 100644
--- a/shader/shared/bbox.wgsl
+++ b/shader/shared/bbox.wgsl
@@ -9,7 +9,7 @@
     y0: i32,
     x1: i32,
     y1: i32,
-    linewidth: f32,
+    linewidth: u32,
     trans_ix: u32,
 }
 
diff --git a/src/encoding/encoding.rs b/src/encoding/encoding.rs
index 03ca730..7d662da 100644
--- a/src/encoding/encoding.rs
+++ b/src/encoding/encoding.rs
@@ -89,7 +89,7 @@
         self.color_stops.clear();
         if !is_fragment {
             self.transforms.push(Transform::IDENTITY);
-            self.linewidths.push(-1.0);
+            self.linewidths.push(f32::INFINITY);
         }
     }
 
@@ -181,6 +181,22 @@
         }
     }
 
+    pub fn encode_fill(&mut self, even_odd: bool) {
+        if even_odd {
+            self.encode_linewidth(f32::from_bits(0x7f800000));
+        } else {
+            self.encode_linewidth(f32::from_bits(0x7fc00000));
+        }
+    }
+
+    pub fn encode_stroke(&mut self, width: f32, scaled: bool) {
+        if scaled {
+            self.encode_linewidth(width);
+        } else {
+            self.encode_linewidth(-width);
+        }
+    }
+
     /// Encodes a transform.
     ///
     /// If the given transform is different from the current one, encodes it and
diff --git a/src/encoding/glyph_cache.rs b/src/encoding/glyph_cache.rs
index f919afb..feb124b 100644
--- a/src/encoding/glyph_cache.rs
+++ b/src/encoding/glyph_cache.rs
@@ -55,9 +55,9 @@
         let mut encode_glyph = || {
             let start = encoding_cache.stream_offsets();
             match style {
-                Style::Fill(Fill::NonZero) => encoding_cache.encode_linewidth(-1.0),
-                Style::Fill(Fill::EvenOdd) => encoding_cache.encode_linewidth(-2.0),
-                Style::Stroke(stroke) => encoding_cache.encode_linewidth(stroke.width),
+                Style::Fill(Fill::NonZero) => encoding_cache.encode_fill(false),
+                Style::Fill(Fill::EvenOdd) => encoding_cache.encode_fill(true),
+                Style::Stroke(stroke) => encoding_cache.encode_stroke(stroke.width, stroke.scale),
             }
             let mut path = crate::glyph::PathEncoderPen(encoding_cache.encode_path(is_fill));
             scaler
diff --git a/src/glyph.rs b/src/glyph.rs
index b398e6f..016e7cd 100644
--- a/src/glyph.rs
+++ b/src/glyph.rs
@@ -101,9 +101,9 @@
 
     pub fn encode_glyph(&mut self, gid: u16, style: &Style, encoding: &mut Encoding) -> Option<()> {
         match style {
-            Style::Fill(Fill::NonZero) => encoding.encode_linewidth(-1.0),
-            Style::Fill(Fill::EvenOdd) => encoding.encode_linewidth(-2.0),
-            Style::Stroke(stroke) => encoding.encode_linewidth(stroke.width),
+            Style::Fill(Fill::NonZero) => encoding.encode_fill(false),
+            Style::Fill(Fill::EvenOdd) => encoding.encode_fill(true),
+            Style::Stroke(stroke) => encoding.encode_stroke(stroke.width, stroke.scale),
         }
         let mut path = PathEncoderPen(encoding.encode_path(matches!(style, Style::Fill(_))));
         self.scaler.outline(GlyphId::new(gid), &mut path).ok()?;
diff --git a/src/scene.rs b/src/scene.rs
index ce777b8..eef9fc7 100644
--- a/src/scene.rs
+++ b/src/scene.rs
@@ -101,7 +101,7 @@
         let blend = blend.into();
         self.scene
             .encode_transform(Transform::from_kurbo(&transform));
-        self.scene.encode_linewidth(-1.0);
+        self.scene.encode_fill(false);
         if !self.scene.encode_shape(shape, true) {
             // If the layer shape is invalid, encode a valid empty path. This suppresses
             // all drawing until the layer is popped.
@@ -127,10 +127,7 @@
     ) {
         self.scene
             .encode_transform(Transform::from_kurbo(&transform));
-        self.scene.encode_linewidth(match style {
-            Fill::NonZero => -1.0,
-            Fill::EvenOdd => -2.0,
-        });
+        self.scene.encode_fill(style == Fill::EvenOdd);
         if self.scene.encode_shape(shape, true) {
             if let Some(brush_transform) = brush_transform {
                 if self
@@ -155,7 +152,7 @@
     ) {
         self.scene
             .encode_transform(Transform::from_kurbo(&transform));
-        self.scene.encode_linewidth(style.width);
+        self.scene.encode_stroke(style.width, style.scale);
         if self.scene.encode_shape(shape, false) {
             if let Some(brush_transform) = brush_transform {
                 if self