rename sb/builder to scene
diff --git a/examples/scenes/src/lib.rs b/examples/scenes/src/lib.rs
index b19f723..b0974f5 100644
--- a/examples/scenes/src/lib.rs
+++ b/examples/scenes/src/lib.rs
@@ -43,12 +43,12 @@
 }
 
 pub trait TestScene {
-    fn render(&mut self, sb: &mut Scene, params: &mut SceneParams);
+    fn render(&mut self, scene: &mut Scene, params: &mut SceneParams);
 }
 
 impl<F: FnMut(&mut Scene, &mut SceneParams)> TestScene for F {
-    fn render(&mut self, sb: &mut Scene, params: &mut SceneParams) {
-        self(sb, params);
+    fn render(&mut self, scene: &mut Scene, params: &mut SceneParams) {
+        self(scene, params);
     }
 }
 
diff --git a/examples/scenes/src/mmark.rs b/examples/scenes/src/mmark.rs
index ef8cf81..1657338 100644
--- a/examples/scenes/src/mmark.rs
+++ b/examples/scenes/src/mmark.rs
@@ -67,7 +67,7 @@
 }
 
 impl TestScene for MMark {
-    fn render(&mut self, sb: &mut Scene, params: &mut SceneParams) {
+    fn render(&mut self, scene: &mut Scene, params: &mut SceneParams) {
         let c = params.complexity;
         let n = if c < 10 {
             (c + 1) * 1000
@@ -90,7 +90,7 @@
             if element.is_split || i == len {
                 // This gets color and width from the last element, original
                 // gets it from the first, but this should not matter.
-                sb.stroke(
+                scene.stroke(
                     &Stroke::new(element.width),
                     Affine::IDENTITY,
                     element.color,
@@ -105,7 +105,7 @@
         }
         let label = format!("mmark test: {} path elements (up/down to adjust)", n);
         params.text.add(
-            sb,
+            scene,
             None,
             40.0,
             None,
diff --git a/examples/scenes/src/simple_text.rs b/examples/scenes/src/simple_text.rs
index 7bded02..b109952 100644
--- a/examples/scenes/src/simple_text.rs
+++ b/examples/scenes/src/simple_text.rs
@@ -46,7 +46,7 @@
     #[allow(clippy::too_many_arguments)]
     pub fn add_run<'a>(
         &mut self,
-        builder: &mut Scene,
+        scene: &mut Scene,
         font: Option<&Font>,
         size: f32,
         brush: impl Into<BrushRef<'a>>,
@@ -56,7 +56,7 @@
         text: &str,
     ) {
         self.add_var_run(
-            builder,
+            scene,
             font,
             size,
             &[],
@@ -71,7 +71,7 @@
     #[allow(clippy::too_many_arguments)]
     pub fn add_var_run<'a>(
         &mut self,
-        builder: &mut Scene,
+        scene: &mut Scene,
         font: Option<&Font>,
         size: f32,
         variations: &[(&str, f32)],
@@ -99,7 +99,7 @@
         let glyph_metrics = font_ref.glyph_metrics(font_size, &var_loc);
         let mut pen_x = 0f32;
         let mut pen_y = 0f32;
-        builder
+        scene
             .draw_glyphs(font)
             .font_size(size)
             .transform(transform)
@@ -129,7 +129,7 @@
 
     pub fn add(
         &mut self,
-        builder: &mut Scene,
+        scene: &mut Scene,
         font: Option<&Font>,
         size: f32,
         brush: Option<&Brush>,
@@ -139,7 +139,7 @@
         use vello::peniko::{Color, Fill};
         let brush = brush.unwrap_or(&Brush::Solid(Color::WHITE));
         self.add_run(
-            builder,
+            scene,
             font,
             size,
             brush,
diff --git a/examples/scenes/src/svg.rs b/examples/scenes/src/svg.rs
index 03a2783..0a5334c 100644
--- a/examples/scenes/src/svg.rs
+++ b/examples/scenes/src/svg.rs
@@ -97,7 +97,7 @@
         let start = Instant::now();
         let mut new_scene = Scene::new();
         vello_svg::render_tree(&mut new_scene, &svg);
-        let resolution = Vec2::new(svg.size.width(), svg.size.height());
+        let resolution = Vec2::new(svg.size.width() as f64, svg.size.height() as f64);
         eprintln!("Encoded svg {name} in {:?}", start.elapsed());
         (new_scene, resolution)
     }
@@ -109,9 +109,9 @@
     #[cfg(not(target_arch = "wasm32"))]
     let mut has_started_parse = false;
     let mut contents = Some(contents);
-    move |builder, params| {
+    move |scene, params| {
         if let Some((scene_frag, resolution)) = cached_scene.as_mut() {
-            builder.append(scene_frag, None);
+            scene.append(scene_frag, None);
             params.resolution = Some(*resolution);
             return;
         }
@@ -119,7 +119,7 @@
             let contents = contents.take().unwrap();
             let contents = contents();
             let (scene_frag, resolution) = render_svg_contents(&name, contents.as_ref());
-            builder.append(&scene_frag, None);
+            scene.append(&scene_frag, None);
             params.resolution = Some(resolution);
             cached_scene = Some((scene_frag, resolution));
             return;
@@ -144,12 +144,12 @@
             use std::sync::mpsc::RecvTimeoutError;
             match recv {
                 Result::Ok((scene_frag, resolution)) => {
-                    builder.append(&scene_frag, None);
+                    scene.append(&scene_frag, None);
                     params.resolution = Some(resolution);
                     cached_scene = Some((scene_frag, resolution));
                 }
                 Err(RecvTimeoutError::Timeout) => params.text.add(
-                    builder,
+                    scene,
                     None,
                     48.,
                     None,
diff --git a/examples/scenes/src/test_scenes.rs b/examples/scenes/src/test_scenes.rs
index 1afa6aa..b43746e 100644
--- a/examples/scenes/src/test_scenes.rs
+++ b/examples/scenes/src/test_scenes.rs
@@ -67,7 +67,7 @@
 
 // Scenes
 
-fn funky_paths(sb: &mut Scene, _: &mut SceneParams) {
+fn funky_paths(scene: &mut Scene, _: &mut SceneParams) {
     use PathEl::*;
     let missing_movetos = [
         MoveTo((0., 0.).into()),
@@ -79,28 +79,28 @@
     ];
     let only_movetos = [MoveTo((0.0, 0.0).into()), MoveTo((100.0, 100.0).into())];
     let empty: [PathEl; 0] = [];
-    sb.fill(
+    scene.fill(
         Fill::NonZero,
         Affine::translate((100.0, 100.0)),
         Color::rgb8(0, 0, 255),
         None,
         &missing_movetos,
     );
-    sb.fill(
+    scene.fill(
         Fill::NonZero,
         Affine::IDENTITY,
         Color::rgb8(0, 0, 255),
         None,
         &empty,
     );
-    sb.fill(
+    scene.fill(
         Fill::NonZero,
         Affine::IDENTITY,
         Color::rgb8(0, 0, 255),
         None,
         &only_movetos,
     );
-    sb.stroke(
+    scene.stroke(
         &Stroke::new(8.0),
         Affine::translate((100.0, 100.0)),
         Color::rgb8(0, 255, 255),
@@ -111,7 +111,7 @@
 
 fn stroke_styles(transform: Affine) -> impl FnMut(&mut Scene, &mut SceneParams) {
     use PathEl::*;
-    move |sb, params| {
+    move |scene, params| {
         let colors = [
             Color::rgb8(140, 181, 236),
             Color::rgb8(246, 236, 202),
@@ -153,14 +153,14 @@
         for start in cap_styles {
             for end in cap_styles {
                 params.text.add(
-                    sb,
+                    scene,
                     None,
                     12.,
                     None,
                     Affine::translate((0., y)) * t,
                     &format!("Start cap: {:?}, End cap: {:?}", start, end),
                 );
-                sb.stroke(
+                scene.stroke(
                     &Stroke::new(20.).with_start_cap(start).with_end_cap(end),
                     Affine::translate((0., y + 30.)) * t * transform,
                     colors[color_idx],
@@ -178,14 +178,14 @@
         for start in cap_styles {
             for end in cap_styles {
                 params.text.add(
-                    sb,
+                    scene,
                     None,
                     12.,
                     None,
                     Affine::translate((0., y)) * t,
                     &format!("Dashing - Start cap: {:?}, End cap: {:?}", start, end),
                 );
-                sb.stroke(
+                scene.stroke(
                     &Stroke::new(20.)
                         .with_start_cap(start)
                         .with_end_cap(end)
@@ -206,14 +206,14 @@
         for cap in cap_styles {
             for join in join_styles {
                 params.text.add(
-                    sb,
+                    scene,
                     None,
                     12.,
                     None,
                     Affine::translate((0., y)) * t,
                     &format!("Caps: {:?}, Joins: {:?}", cap, join),
                 );
-                sb.stroke(
+                scene.stroke(
                     &Stroke::new(20.).with_caps(cap).with_join(join),
                     Affine::translate((0., y + 30.)) * t * transform,
                     colors[color_idx],
@@ -230,14 +230,14 @@
         y = 0.;
         for ml in miter_limits {
             params.text.add(
-                sb,
+                scene,
                 None,
                 12.,
                 None,
                 Affine::translate((0., y)) * t,
                 &format!("Miter limit: {}", ml),
             );
-            sb.stroke(
+            scene.stroke(
                 &Stroke::new(10.)
                     .with_caps(Cap::Butt)
                     .with_join(Join::Miter)
@@ -254,7 +254,7 @@
         // Closed paths
         for (i, join) in join_styles.iter().enumerate() {
             params.text.add(
-                sb,
+                scene,
                 None,
                 12.,
                 None,
@@ -262,7 +262,7 @@
                 &format!("Closed path with join: {:?}", join),
             );
             // The cap style is not important since a closed path shouldn't have any caps.
-            sb.stroke(
+            scene.stroke(
                 &Stroke::new(10.)
                     .with_caps(cap_styles[i])
                     .with_join(*join)
@@ -280,7 +280,7 @@
 
 // This test has been adapted from Skia's "trickycubicstrokes" GM slide which can be found at
 // `github.com/google/skia/blob/0d4d11451c4f4e184305cbdbd67f6b3edfa4b0e3/gm/trickycubicstrokes.cpp`
-fn tricky_strokes(sb: &mut Scene, _: &mut SceneParams) {
+fn tricky_strokes(scene: &mut Scene, _: &mut SceneParams) {
     use PathEl::*;
     let colors = [
         Color::rgb8(140, 181, 236),
@@ -352,7 +352,7 @@
         let cell = Rect::new(x, y, x + CELL_SIZE, y + CELL_SIZE);
         let bounds = stroke_bounds(&cubic);
         let (t, s) = map_rect_to_rect(&bounds, &cell);
-        sb.stroke(
+        scene.stroke(
             &Stroke::new(STROKE_WIDTH / s)
                 .with_caps(Cap::Butt)
                 .with_join(Join::Miter),
@@ -368,7 +368,7 @@
     }
 }
 
-fn fill_types(sb: &mut Scene, params: &mut SceneParams) {
+fn fill_types(scene: &mut Scene, params: &mut SceneParams) {
     use PathEl::*;
     let rect = Rect::from_origin_size(Point::new(0., 0.), (500., 500.));
     let star = [
@@ -397,16 +397,16 @@
     ];
     for (i, rule) in rules.iter().enumerate() {
         let t = Affine::translate(((i % 2) as f64 * 306., (i / 2) as f64 * 340.)) * t;
-        params.text.add(sb, None, 24., None, t, rule.1);
+        params.text.add(scene, None, 24., None, t, rule.1);
         let t = Affine::translate((0., 5.)) * t * scale;
-        sb.fill(
+        scene.fill(
             Fill::NonZero,
             t,
             &Brush::Solid(Color::rgb8(128, 128, 128)),
             None,
             &rect,
         );
-        sb.fill(
+        scene.fill(
             rule.0,
             Affine::translate((0., 10.)) * t,
             Color::YELLOW,
@@ -419,30 +419,30 @@
     let t = Affine::translate((700., 0.)) * t;
     for (i, rule) in rules.iter().enumerate() {
         let t = Affine::translate(((i % 2) as f64 * 306., (i / 2) as f64 * 340.)) * t;
-        params.text.add(sb, None, 24., None, t, rule.1);
+        params.text.add(scene, None, 24., None, t, rule.1);
         let t = Affine::translate((0., 5.)) * t * scale;
-        sb.fill(
+        scene.fill(
             Fill::NonZero,
             t,
             &Brush::Solid(Color::rgb8(128, 128, 128)),
             None,
             &rect,
         );
-        sb.fill(
+        scene.fill(
             rule.0,
             Affine::translate((0., 10.)) * t,
             Color::YELLOW,
             None,
             &rule.2,
         );
-        sb.fill(
+        scene.fill(
             rule.0,
             Affine::translate((0., 10.)) * t * Affine::rotate(0.06),
             Color::rgba(0., 1., 0.7, 0.6),
             None,
             &rule.2,
         );
-        sb.fill(
+        scene.fill(
             rule.0,
             Affine::translate((0., 10.)) * t * Affine::rotate(-0.06),
             Color::rgba(0.9, 0.7, 0.5, 0.6),
@@ -452,17 +452,17 @@
     }
 }
 
-fn cardioid_and_friends(sb: &mut Scene, _: &mut SceneParams) {
-    render_cardioid(sb);
-    render_clip_test(sb);
-    render_alpha_test(sb);
-    //render_tiger(sb, false);
+fn cardioid_and_friends(scene: &mut Scene, _: &mut SceneParams) {
+    render_cardioid(scene);
+    render_clip_test(scene);
+    render_alpha_test(scene);
+    //render_tiger(scene, false);
 }
 
 fn longpathdash(cap: Cap) -> impl FnMut(&mut Scene, &mut SceneParams) {
     use std::f64::consts::PI;
     use PathEl::*;
-    move |sb, _| {
+    move |scene, _| {
         let mut path = BezPath::new();
         let mut x = 32;
         while x < 256 {
@@ -491,7 +491,7 @@
             }
             x += 16;
         }
-        sb.stroke(
+        scene.stroke(
             &Stroke::new(1.0)
                 .with_caps(cap)
                 .with_join(Join::Bevel)
@@ -504,7 +504,7 @@
     }
 }
 
-fn animated_text(sb: &mut Scene, params: &mut SceneParams) {
+fn animated_text(scene: &mut Scene, params: &mut SceneParams) {
     // Uses the static array address as a cache key for expedience. Real code
     // should use a better strategy.
     let piet_logo = params
@@ -522,7 +522,7 @@
         LineTo((79.0, 90.0).into()),
         ClosePath,
     ];
-    sb.fill(
+    scene.fill(
         Fill::NonZero,
         Affine::IDENTITY,
         &Brush::Solid(Color::rgb8(128, 128, 128)),
@@ -532,7 +532,7 @@
     let text_size = 60.0 + 40.0 * (params.time as f32).sin();
     let s = "\u{1f600}hello vello text!";
     params.text.add(
-        sb,
+        scene,
         None,
         text_size,
         None,
@@ -540,7 +540,7 @@
         s,
     );
     params.text.add_run(
-        sb,
+        scene,
         None,
         text_size,
         Color::WHITE,
@@ -554,7 +554,7 @@
     let weight = t * 700.0 + 200.0;
     let width = t * 150.0 + 50.0;
     params.text.add_var_run(
-        sb,
+        scene,
         None,
         72.0,
         &[("wght", weight), ("wdth", width)],
@@ -570,14 +570,14 @@
     let mut p1 = center;
     p1.x += 400.0 * th.cos();
     p1.y += 400.0 * th.sin();
-    sb.stroke(
+    scene.stroke(
         &Stroke::new(5.0),
         Affine::IDENTITY,
         &Brush::Solid(Color::rgb8(128, 0, 0)),
         None,
         &[PathEl::MoveTo(center), PathEl::LineTo(p1)],
     );
-    sb.fill(
+    scene.fill(
         Fill::NonZero,
         Affine::translate((150.0, 150.0)) * Affine::scale(0.2),
         Color::RED,
@@ -585,50 +585,50 @@
         &rect,
     );
     let alpha = params.time.sin() as f32 * 0.5 + 0.5;
-    sb.push_layer(Mix::Normal, alpha, Affine::IDENTITY, &rect);
-    sb.fill(
+    scene.push_layer(Mix::Normal, alpha, Affine::IDENTITY, &rect);
+    scene.fill(
         Fill::NonZero,
         Affine::translate((100.0, 100.0)) * Affine::scale(0.2),
         Color::BLUE,
         None,
         &rect,
     );
-    sb.fill(
+    scene.fill(
         Fill::NonZero,
         Affine::translate((200.0, 200.0)) * Affine::scale(0.2),
         Color::GREEN,
         None,
         &rect,
     );
-    sb.pop_layer();
-    sb.fill(
+    scene.pop_layer();
+    scene.fill(
         Fill::NonZero,
         Affine::translate((400.0, 100.0)),
         Color::PURPLE,
         None,
         &star,
     );
-    sb.fill(
+    scene.fill(
         Fill::EvenOdd,
         Affine::translate((500.0, 100.0)),
         Color::PURPLE,
         None,
         &star,
     );
-    sb.draw_image(
+    scene.draw_image(
         &piet_logo,
         Affine::translate((800.0, 50.0)) * Affine::rotate(20f64.to_radians()),
     );
 }
 
-fn brush_transform(sb: &mut Scene, params: &mut SceneParams) {
+fn brush_transform(scene: &mut Scene, params: &mut SceneParams) {
     let th = params.time;
     let linear = Gradient::new_linear((0.0, 0.0), (0.0, 200.0)).with_stops([
         Color::RED,
         Color::GREEN,
         Color::BLUE,
     ]);
-    sb.fill(
+    scene.fill(
         Fill::NonZero,
         Affine::rotate(25f64.to_radians()) * Affine::scale_non_uniform(2.0, 1.0),
         &Gradient::new_radial((200.0, 200.0), 80.0).with_stops([
@@ -639,14 +639,14 @@
         None,
         &Rect::from_origin_size((100.0, 100.0), (200.0, 200.0)),
     );
-    sb.fill(
+    scene.fill(
         Fill::NonZero,
         Affine::translate((200.0, 600.0)),
         &linear,
         Some(around_center(Affine::rotate(th), Point::new(200.0, 100.0))),
         &Rect::from_origin_size(Point::default(), (400.0, 200.0)),
     );
-    sb.stroke(
+    scene.stroke(
         &Stroke::new(40.0),
         Affine::translate((800.0, 600.0)),
         &linear,
@@ -655,8 +655,8 @@
     );
 }
 
-fn gradient_extend(sb: &mut Scene, params: &mut SceneParams) {
-    fn square(sb: &mut Scene, is_radial: bool, transform: Affine, extend: Extend) {
+fn gradient_extend(scene: &mut Scene, params: &mut SceneParams) {
+    fn square(scene: &mut Scene, is_radial: bool, transform: Affine, extend: Extend) {
         let colors = [Color::RED, Color::rgb8(0, 255, 0), Color::BLUE];
         let width = 300f64;
         let height = 300f64;
@@ -673,7 +673,7 @@
                 .with_extend(extend)
                 .into()
         };
-        sb.fill(
+        scene.fill(
             Fill::NonZero,
             transform,
             &gradient,
@@ -686,13 +686,13 @@
         for y in 0..2 {
             let is_radial = y & 1 != 0;
             let transform = Affine::translate((x as f64 * 350.0 + 50.0, y as f64 * 350.0 + 100.0));
-            square(sb, is_radial, transform, *extend);
+            square(scene, is_radial, transform, *extend);
         }
     }
     for (i, label) in ["Pad", "Repeat", "Reflect"].iter().enumerate() {
         let x = i as f64 * 350.0 + 50.0;
         params.text.add(
-            sb,
+            scene,
             None,
             32.0,
             Some(&Color::WHITE.into()),
@@ -703,9 +703,9 @@
 }
 
 #[allow(clippy::too_many_arguments)]
-fn two_point_radial(sb: &mut Scene, _params: &mut SceneParams) {
+fn two_point_radial(scene: &mut Scene, _params: &mut SceneParams) {
     fn make(
-        sb: &mut Scene,
+        scene: &mut Scene,
         x0: f64,
         y0: f64,
         r0: f32,
@@ -719,8 +719,8 @@
         let width = 400f64;
         let height = 200f64;
         let rect = Rect::new(0.0, 0.0, width, height);
-        sb.fill(Fill::NonZero, transform, Color::WHITE, None, &rect);
-        sb.fill(
+        scene.fill(Fill::NonZero, transform, Color::WHITE, None, &rect);
+        scene.fill(
             Fill::NonZero,
             transform,
             &Gradient::new_two_point_radial((x0, y0), r0, (x1, y1), r1)
@@ -732,14 +732,14 @@
         let r0 = r0 as f64 - 1.0;
         let r1 = r1 as f64 - 1.0;
         let stroke_width = 1.0;
-        sb.stroke(
+        scene.stroke(
             &Stroke::new(stroke_width),
             transform,
             Color::BLACK,
             None,
             &Ellipse::new((x0, y0), (r0, r0), 0.0),
         );
-        sb.stroke(
+        scene.stroke(
             &Stroke::new(stroke_width),
             transform,
             Color::BLACK,
@@ -761,7 +761,7 @@
         let r0 = 20.0;
         let r1 = 50.0;
         make(
-            sb,
+            scene,
             x0,
             y,
             r0,
@@ -783,7 +783,7 @@
         let r0 = 20.0;
         let r1 = 50.0;
         make(
-            sb,
+            scene,
             x1,
             y,
             r1,
@@ -805,7 +805,7 @@
         let r0 = 50.0;
         let r1 = 50.0;
         make(
-            sb,
+            scene,
             x0,
             y,
             r0,
@@ -828,7 +828,7 @@
         let y1 = 100.0;
         let r1 = 95.0;
         make(
-            sb,
+            scene,
             x0,
             y0,
             r0,
@@ -854,7 +854,7 @@
         let p0 = Point::new(x1, y1)
             + ((Point::new(x0, y0) - Point::new(x1, y1)).normalize() * (r1 - r0));
         make(
-            sb,
+            scene,
             p0.x,
             p0.y,
             r0 as f32,
@@ -867,7 +867,7 @@
     }
 }
 
-fn blend_grid(sb: &mut Scene, _: &mut SceneParams) {
+fn blend_grid(scene: &mut Scene, _: &mut SceneParams) {
     const BLEND_MODES: &[Mix] = &[
         Mix::Normal,
         Mix::Multiply,
@@ -891,13 +891,13 @@
         let j = ix / 4;
         let transform = Affine::translate((i as f64 * 225., j as f64 * 225.));
         let square = blend_square(blend.into());
-        sb.append(&square, Some(transform));
+        scene.append(&square, Some(transform));
     }
 }
 
 // Support functions
 
-fn render_cardioid(sb: &mut Scene) {
+fn render_cardioid(scene: &mut Scene) {
     let n = 601;
     let dth = std::f64::consts::PI * 2.0 / (n as f64);
     let center = Point::new(1024.0, 768.0);
@@ -915,7 +915,7 @@
         path.push(PathEl::MoveTo(p0));
         path.push(PathEl::LineTo(p1));
     }
-    sb.stroke(
+    scene.stroke(
         &Stroke::new(2.0),
         Affine::IDENTITY,
         Color::rgb8(0, 0, 255),
@@ -924,7 +924,7 @@
     );
 }
 
-fn render_clip_test(sb: &mut Scene) {
+fn render_clip_test(scene: &mut Scene) {
     const N: usize = 16;
     const X0: f64 = 50.0;
     const Y0: f64 = 450.0;
@@ -943,10 +943,10 @@
             PathEl::LineTo((X0, Y1).into()),
             PathEl::ClosePath,
         ];
-        sb.push_layer(Mix::Clip, 1.0, Affine::IDENTITY, &path);
+        scene.push_layer(Mix::Clip, 1.0, Affine::IDENTITY, &path);
     }
     let rect = Rect::new(X0, Y0, X1, Y1);
-    sb.fill(
+    scene.fill(
         Fill::NonZero,
         Affine::IDENTITY,
         &Brush::Solid(Color::rgb8(0, 255, 0)),
@@ -954,48 +954,48 @@
         &rect,
     );
     for _ in 0..N {
-        sb.pop_layer();
+        scene.pop_layer();
     }
 }
 
-fn render_alpha_test(sb: &mut Scene) {
+fn render_alpha_test(scene: &mut Scene) {
     // Alpha compositing tests.
-    sb.fill(
+    scene.fill(
         Fill::NonZero,
         Affine::IDENTITY,
         Color::rgb8(255, 0, 0),
         None,
         &make_diamond(1024.0, 100.0),
     );
-    sb.fill(
+    scene.fill(
         Fill::NonZero,
         Affine::IDENTITY,
         Color::rgba8(0, 255, 0, 0x80),
         None,
         &make_diamond(1024.0, 125.0),
     );
-    sb.push_layer(
+    scene.push_layer(
         Mix::Clip,
         1.0,
         Affine::IDENTITY,
         &make_diamond(1024.0, 150.0),
     );
-    sb.fill(
+    scene.fill(
         Fill::NonZero,
         Affine::IDENTITY,
         Color::rgba8(0, 0, 255, 0x80),
         None,
         &make_diamond(1024.0, 175.0),
     );
-    sb.pop_layer();
+    scene.pop_layer();
 }
 
-fn render_blend_square(sb: &mut Scene, blend: BlendMode, transform: Affine) {
+fn render_blend_square(scene: &mut Scene, blend: BlendMode, transform: Affine) {
     // Inspired by https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
     let rect = Rect::from_origin_size(Point::new(0., 0.), (200., 200.));
     let linear =
         Gradient::new_linear((0.0, 0.0), (200.0, 0.0)).with_stops([Color::BLACK, Color::WHITE]);
-    sb.fill(Fill::NonZero, transform, &linear, None, &rect);
+    scene.fill(Fill::NonZero, transform, &linear, None, &rect);
     const GRADIENTS: &[(f64, f64, Color)] = &[
         (150., 0., Color::rgb8(255, 240, 64)),
         (175., 100., Color::rgb8(255, 96, 240)),
@@ -1005,33 +1005,33 @@
         let mut color2 = *c;
         color2.a = 0;
         let radial = Gradient::new_radial((*x, *y), 100.0).with_stops([*c, color2]);
-        sb.fill(Fill::NonZero, transform, &radial, None, &rect);
+        scene.fill(Fill::NonZero, transform, &radial, None, &rect);
     }
     const COLORS: &[Color] = &[
         Color::rgb8(255, 0, 0),
         Color::rgb8(0, 255, 0),
         Color::rgb8(0, 0, 255),
     ];
-    sb.push_layer(Mix::Normal, 1.0, transform, &rect);
+    scene.push_layer(Mix::Normal, 1.0, transform, &rect);
     for (i, c) in COLORS.iter().enumerate() {
         let linear = Gradient::new_linear((0.0, 0.0), (0.0, 200.0)).with_stops([Color::WHITE, *c]);
-        sb.push_layer(blend, 1.0, transform, &rect);
+        scene.push_layer(blend, 1.0, transform, &rect);
         // squash the ellipse
         let a = transform
             * Affine::translate((100., 100.))
             * Affine::rotate(std::f64::consts::FRAC_PI_3 * (i * 2 + 1) as f64)
             * Affine::scale_non_uniform(1.0, 0.357)
             * Affine::translate((-100., -100.));
-        sb.fill(
+        scene.fill(
             Fill::NonZero,
             a,
             &linear,
             None,
             &Ellipse::new((100., 100.), (90., 90.), 0.),
         );
-        sb.pop_layer();
+        scene.pop_layer();
     }
-    sb.pop_layer();
+    scene.pop_layer();
 }
 
 fn blend_square(blend: BlendMode) -> Scene {
@@ -1040,7 +1040,7 @@
     fragment
 }
 
-fn conflation_artifacts(sb: &mut Scene, _: &mut SceneParams) {
+fn conflation_artifacts(scene: &mut Scene, _: &mut SceneParams) {
     use PathEl::*;
     const N: f64 = 50.0;
     const S: f64 = 4.0;
@@ -1053,7 +1053,7 @@
     let fg_color = Color::rgb8(12, 165, 255);
 
     // Two adjacent triangles touching at diagonal edge with opposing winding numbers
-    sb.fill(
+    scene.fill(
         Fill::NonZero,
         Affine::translate((x, y)) * scale,
         fg_color,
@@ -1074,14 +1074,14 @@
 
     // Adjacent rects, opposite winding
     y += S * N + 10.0;
-    sb.fill(
+    scene.fill(
         Fill::EvenOdd,
         Affine::translate((x, y)) * scale,
         bg_color,
         None,
         &Rect::new(0.0, 0.0, N, N),
     );
-    sb.fill(
+    scene.fill(
         Fill::EvenOdd,
         Affine::translate((x, y)) * scale,
         fg_color,
@@ -1102,14 +1102,14 @@
 
     // Adjacent rects, same winding
     y += S * N + 10.0;
-    sb.fill(
+    scene.fill(
         Fill::EvenOdd,
         Affine::translate((x, y)) * scale,
         bg_color,
         None,
         &Rect::new(0.0, 0.0, N, N),
     );
-    sb.fill(
+    scene.fill(
         Fill::EvenOdd,
         Affine::translate((x, y)) * scale,
         fg_color,
@@ -1129,7 +1129,7 @@
     );
 }
 
-fn labyrinth(sb: &mut Scene, _: &mut SceneParams) {
+fn labyrinth(scene: &mut Scene, _: &mut SceneParams) {
     use PathEl::*;
 
     let rows: &[[u8; 12]] = &[
@@ -1197,7 +1197,7 @@
 
     // Note the artifacts are clearly visible at a fractional pixel offset/translation. They
     // disappear if the translation amount below is a whole number.
-    sb.fill(
+    scene.fill(
         Fill::NonZero,
         Affine::translate((20.5, 20.5)) * Affine::scale(80.0),
         Color::rgba8(0x70, 0x80, 0x80, 0xff),
@@ -1206,7 +1206,7 @@
     );
 }
 
-fn robust_paths(sb: &mut Scene, _: &mut SceneParams) {
+fn robust_paths(scene: &mut Scene, _: &mut SceneParams) {
     let mut path = BezPath::new();
     path.move_to((16.0, 16.0));
     path.line_to((32.0, 16.0));
@@ -1253,8 +1253,8 @@
     path.line_to((256.0, 24.5));
     path.line_to((241.0, 24.5));
     path.close_path();
-    sb.fill(Fill::NonZero, Affine::IDENTITY, Color::YELLOW, None, &path);
-    sb.fill(
+    scene.fill(Fill::NonZero, Affine::IDENTITY, Color::YELLOW, None, &path);
+    scene.fill(
         Fill::EvenOdd,
         Affine::translate((300.0, 0.0)),
         Color::LIME,
@@ -1267,14 +1267,14 @@
     path.line_to((260.0, 40.0));
     path.line_to((260.0, 4.0));
     path.close_path();
-    sb.fill(
+    scene.fill(
         Fill::NonZero,
         Affine::translate((0.0, 100.0)),
         Color::YELLOW,
         None,
         &path,
     );
-    sb.fill(
+    scene.fill(
         Fill::EvenOdd,
         Affine::translate((300.0, 100.0)),
         Color::LIME,
@@ -1283,13 +1283,13 @@
     );
 }
 
-fn base_color_test(sb: &mut Scene, params: &mut SceneParams) {
+fn base_color_test(scene: &mut Scene, params: &mut SceneParams) {
     // Cycle through the hue value every 5 seconds (t % 5) * 360/5
     let color = Color::hlc((params.time % 5.0) * 72.0, 80.0, 80.0);
     params.base_color = Some(color);
 
     // Blend a white square over it.
-    sb.fill(
+    scene.fill(
         Fill::NonZero,
         Affine::IDENTITY,
         Color::rgba8(255, 255, 255, 128),
@@ -1298,7 +1298,7 @@
     );
 }
 
-fn clip_test(sb: &mut Scene, params: &mut SceneParams) {
+fn clip_test(scene: &mut Scene, params: &mut SceneParams) {
     let clip = {
         const X0: f64 = 50.0;
         const Y0: f64 = 0.0;
@@ -1313,12 +1313,12 @@
             PathEl::ClosePath,
         ]
     };
-    sb.push_layer(Mix::Clip, 1.0, Affine::IDENTITY, &clip);
+    scene.push_layer(Mix::Clip, 1.0, Affine::IDENTITY, &clip);
     {
         let text_size = 60.0 + 40.0 * (params.time as f32).sin();
         let s = "Some clipped text!";
         params.text.add(
-            sb,
+            scene,
             None,
             text_size,
             None,
@@ -1326,7 +1326,7 @@
             s,
         );
     }
-    sb.pop_layer();
+    scene.pop_layer();
 
     let large_background_rect = kurbo::Rect::new(-1000.0, -1000.0, 2000.0, 2000.0);
     let inside_clip_rect = kurbo::Rect::new(11.0, 13.399999999999999, 59.0, 56.6);
@@ -1339,7 +1339,7 @@
     let clip_rect = kurbo::Rect::new(0.0, 0.0, 74.4, 339.20000000000005);
     let scale = 2.0;
 
-    sb.push_layer(
+    scene.push_layer(
         BlendMode {
             mix: peniko::Mix::Normal,
             compose: peniko::Compose::SrcOver,
@@ -1349,14 +1349,14 @@
         &clip_rect,
     );
 
-    sb.fill(
+    scene.fill(
         peniko::Fill::NonZero,
         kurbo::Affine::new([scale, 0.0, 0.0, scale, 27.07470703125, 176.40660533027858]),
         peniko::Color::rgb8(0, 0, 255),
         None,
         &large_background_rect,
     );
-    sb.fill(
+    scene.fill(
         peniko::Fill::NonZero,
         kurbo::Affine::new([
             scale,
@@ -1370,7 +1370,7 @@
         None,
         &inside_clip_rect,
     );
-    sb.fill(
+    scene.fill(
         peniko::Fill::NonZero,
         kurbo::Affine::new([
             scale,
@@ -1385,7 +1385,7 @@
         &outside_clip_rect,
     );
 
-    sb.pop_layer();
+    scene.pop_layer();
 }
 
 fn around_center(xform: Affine, center: Point) -> Affine {
@@ -1403,7 +1403,7 @@
     ]
 }
 
-fn splash_screen(sb: &mut Scene, params: &mut SceneParams) {
+fn splash_screen(scene: &mut Scene, params: &mut SceneParams) {
     let strings = [
         "Vello test",
         "  Arrow keys: switch scenes",
@@ -1418,7 +1418,7 @@
     for (i, s) in strings.iter().enumerate() {
         let text_size = if i == 0 { 60.0 } else { 40.0 };
         params.text.add(
-            sb,
+            scene,
             None,
             text_size,
             None,
@@ -1434,8 +1434,8 @@
         "/../assets/Ghostscript_Tiger.svg"
     ));
     let mut tiger = crate::svg::svg_function_of("Ghostscript Tiger".to_string(), move || contents);
-    move |sb, params| {
-        tiger(sb, params);
-        splash_screen(sb, params);
+    move |scene, params| {
+        tiger(scene, params);
+        splash_screen(scene, params);
     }
 }
diff --git a/examples/with_bevy/src/main.rs b/examples/with_bevy/src/main.rs
index 00004c5..fe30803 100644
--- a/examples/with_bevy/src/main.rs
+++ b/examples/with_bevy/src/main.rs
@@ -2,7 +2,7 @@
 use bevy::utils::synccell::SyncCell;
 use vello::kurbo::{Affine, Point, Rect, Stroke};
 use vello::peniko::{Color, Fill, Gradient};
-use vello::{Renderer, RendererOptions, Scene, SceneBuilder, SceneFragment};
+use vello::{Renderer, RendererOptions, Scene};
 
 use bevy::{
     prelude::*,
@@ -108,7 +108,7 @@
 
 #[derive(Component)]
 // In the future, this will probably connect to the bevy hierarchy with an Affine component
-pub struct VelloFragment(SceneFragment);
+pub struct VelloFragment(Scene);
 
 #[derive(Component)]
 struct VelloScene(Scene, Handle<Image>);
@@ -123,10 +123,7 @@
     fn extract_component(
         (fragment, target): bevy::ecs::query::QueryItem<'_, Self::Query>,
     ) -> Option<Self> {
-        let mut scene = Scene::default();
-        let mut builder = SceneBuilder::for_scene(&mut scene);
-        builder.append(&fragment.0, None);
-        Some(Self(scene, target.0.clone()))
+        Some(Self(fragment.0.clone(), target.0.clone()))
     }
 }
 
@@ -200,7 +197,7 @@
         ..default()
     });
     commands.spawn((
-        VelloFragment(SceneFragment::default()),
+        VelloFragment(Scene::default()),
         VelloTarget(image_handle),
     ));
 }
@@ -215,26 +212,26 @@
 
 fn render_fragment(mut fragment: Query<&mut VelloFragment>, mut frame: Local<usize>) {
     let mut fragment = fragment.single_mut();
-    let mut builder = SceneBuilder::for_fragment(&mut fragment.0);
-    render_brush_transform(&mut builder, *frame);
+    fragment.0.reset();
+    render_brush_transform(&mut fragment.0, *frame);
     *frame += 1;
 }
 
-fn render_brush_transform(sb: &mut SceneBuilder, i: usize) {
+fn render_brush_transform(scene: &mut Scene, i: usize) {
     let th = (std::f64::consts::PI / 180.0) * (i as f64);
     let linear = Gradient::new_linear((0.0, 0.0), (0.0, 200.0)).with_stops([
         Color::RED,
         Color::GREEN,
         Color::BLUE,
     ]);
-    sb.fill(
+    scene.fill(
         Fill::NonZero,
         Affine::translate((106.0, 106.0)),
         &linear,
         Some(around_center(Affine::rotate(th), Point::new(150.0, 150.0))),
         &Rect::from_origin_size(Point::default(), (300.0, 300.0)),
     );
-    sb.stroke(
+    scene.stroke(
         &Stroke::new(106.0),
         Affine::IDENTITY,
         &linear,
diff --git a/examples/with_winit/src/stats.rs b/examples/with_winit/src/stats.rs
index 5911668..2bf9f8d 100644
--- a/examples/with_winit/src/stats.rs
+++ b/examples/with_winit/src/stats.rs
@@ -37,7 +37,7 @@
     #[allow(clippy::too_many_arguments)]
     pub fn draw_layer<'a, T>(
         &self,
-        sb: &mut Scene,
+        scene: &mut Scene,
         text: &mut SimpleText,
         viewport_width: f64,
         viewport_height: f64,
@@ -55,7 +55,7 @@
         let offset = Affine::translate((x_offset, y_offset));
 
         // Draw the background
-        sb.fill(
+        scene.fill(
             Fill::NonZero,
             offset,
             &Brush::Solid(Color::rgba8(0, 0, 0, 200)),
@@ -95,7 +95,7 @@
         let text_size = (text_height * 0.9) as f32;
         for (i, label) in labels.iter().enumerate() {
             text.add(
-                sb,
+                scene,
                 None,
                 text_size,
                 Some(&Brush::Solid(Color::WHITE)),
@@ -104,7 +104,7 @@
             );
         }
         text.add(
-            sb,
+            scene,
             None,
             text_size,
             Some(&Brush::Solid(Color::WHITE)),
@@ -149,7 +149,7 @@
                 ..=33_334 => Color::rgb8(255, 176, 0),
                 _ => Color::rgb8(220, 38, 127),
             };
-            sb.fill(
+            scene.fill(
                 Fill::NonZero,
                 t * Affine::translate((
                     left_margin_padding,
@@ -171,7 +171,7 @@
         for t in thresholds.iter().filter(|&&t| t < display_max) {
             let y = t / display_max;
             text.add(
-                sb,
+                scene,
                 None,
                 thres_text_height as f32,
                 Some(&Brush::Solid(Color::WHITE)),
@@ -182,7 +182,7 @@
                     )),
                 &format!("{}", t),
             );
-            sb.stroke(
+            scene.stroke(
                 &Stroke::new(graph_max_height * 0.01),
                 offset * Affine::translate((left_margin_padding, (1. - y) * graph_max_height)),
                 Color::WHITE,
@@ -270,7 +270,7 @@
 ];
 
 pub fn draw_gpu_profiling(
-    sb: &mut Scene,
+    scene: &mut Scene,
     text: &mut SimpleText,
     viewport_width: f64,
     viewport_height: f64,
@@ -285,7 +285,7 @@
     let offset = Affine::translate((0., y_offset));
 
     // Draw the background
-    sb.fill(
+    scene.fill(
         Fill::NonZero,
         offset,
         &Brush::Solid(Color::rgba8(0, 0, 0, 200)),
@@ -324,7 +324,7 @@
         let text_size = (text_height * 0.9) as f32;
         for (i, label) in labels.iter().enumerate() {
             text.add(
-                sb,
+                scene,
                 None,
                 text_size,
                 Some(&Brush::Solid(Color::WHITE)),
@@ -336,7 +336,7 @@
         let text_size = (text_height * 0.9) as f32;
         for (i, label) in labels.iter().enumerate() {
             text.add(
-                sb,
+                scene,
                 None,
                 text_size,
                 Some(&Brush::Solid(Color::WHITE)),
@@ -367,7 +367,7 @@
 
             let color = COLORS[cur_index % COLORS.len()];
             let x = width * 0.01 + (depth as f64 * depth_width);
-            sb.fill(
+            scene.fill(
                 Fill::NonZero,
                 offset,
                 &Brush::Solid(color),
@@ -402,7 +402,7 @@
                 Duration::from_secs_f64(this_time),
                 profile.label
             );
-            sb.fill(
+            scene.fill(
                 Fill::NonZero,
                 offset,
                 &Brush::Solid(color),
@@ -415,7 +415,7 @@
                 ),
             );
             text.add(
-                sb,
+                scene,
                 None,
                 text_size,
                 Some(&Brush::Solid(text_color)),
@@ -423,7 +423,7 @@
                 &label,
             );
             if !nested && slow {
-                sb.stroke(
+                scene.stroke(
                     &Stroke::new(2.),
                     offset,
                     &Brush::Solid(color),
diff --git a/integrations/vello_svg/src/lib.rs b/integrations/vello_svg/src/lib.rs
index 605fa68..2c3940d 100644
--- a/integrations/vello_svg/src/lib.rs
+++ b/integrations/vello_svg/src/lib.rs
@@ -50,8 +50,8 @@
 /// Calls [`render_tree_with`] with an error handler implementing the above.
 ///
 /// See the [module level documentation](crate#unsupported-features) for a list of some unsupported svg features
-pub fn render_tree(sb: &mut Scene, svg: &usvg::Tree) {
-    render_tree_with(sb, svg, default_error_handler).unwrap_or_else(|e| match e {});
+pub fn render_tree(scene: &mut Scene, svg: &usvg::Tree) {
+    render_tree_with(scene, svg, default_error_handler).unwrap_or_else(|e| match e {});
 }
 
 /// Append a [`usvg::Tree`] into a Vello [`Scene`].
@@ -61,7 +61,7 @@
 ///
 /// See the [module level documentation](crate#unsupported-features) for a list of some unsupported svg features
 pub fn render_tree_with<F: FnMut(&mut Scene, &usvg::Node) -> Result<(), E>, E>(
-    sb: &mut Scene,
+    scene: &mut Scene,
     svg: &usvg::Tree,
     mut on_err: F,
 ) -> Result<(), E> {
@@ -132,7 +132,7 @@
                     if let Some((brush, brush_transform)) =
                         paint_to_brush(&fill.paint, fill.opacity)
                     {
-                        sb.fill(
+                        scene.fill(
                             match fill.rule {
                                 usvg::FillRule::NonZero => Fill::NonZero,
                                 usvg::FillRule::EvenOdd => Fill::EvenOdd,
@@ -143,7 +143,7 @@
                             &local_path,
                         );
                     } else {
-                        on_err(sb, &elt)?;
+                        on_err(scene, &elt)?;
                     }
                 }
                 if let Some(stroke) = &path.stroke {
@@ -152,7 +152,7 @@
                     {
                         // FIXME: handle stroke options such as linecap,
                         // linejoin, etc.
-                        sb.stroke(
+                        scene.stroke(
                             &Stroke::new(stroke.width.get() as f64),
                             transform,
                             &brush,
@@ -160,15 +160,15 @@
                             &local_path,
                         );
                     } else {
-                        on_err(sb, &elt)?;
+                        on_err(scene, &elt)?;
                     }
                 }
             }
             usvg::NodeKind::Image(_) => {
-                on_err(sb, &elt)?;
+                on_err(scene, &elt)?;
             }
             usvg::NodeKind::Text(_) => {
-                on_err(sb, &elt)?;
+                on_err(scene, &elt)?;
             }
         }
     }
@@ -177,7 +177,7 @@
 
 /// Error handler function for [`render_tree_with`] which draws a transparent red box
 /// instead of unsupported SVG features
-pub fn default_error_handler(sb: &mut Scene, node: &usvg::Node) -> Result<(), Infallible> {
+pub fn default_error_handler(scene: &mut Scene, node: &usvg::Node) -> Result<(), Infallible> {
     if let Some(bb) = node.calculate_bbox() {
         let rect = Rect {
             x0: bb.left() as f64,
@@ -185,7 +185,7 @@
             x1: bb.right() as f64,
             y1: bb.bottom() as f64,
         };
-        sb.fill(
+        scene.fill(
             Fill::NonZero,
             Affine::IDENTITY,
             Color::RED.with_alpha_factor(0.5),