.
diff --git a/sparse_strips/vello_common/src/recording.rs b/sparse_strips/vello_common/src/recording.rs
index f34b1cb..80863d6 100644
--- a/sparse_strips/vello_common/src/recording.rs
+++ b/sparse_strips/vello_common/src/recording.rs
@@ -67,6 +67,14 @@
     pub fn strip_start_indices(&self) -> &[usize] {
         &self.strip_start_indices
     }
+
+    /// Takes ownership of all buffers.
+    pub fn take(&mut self) -> (Vec<Strip>, Vec<u8>, Vec<usize>) {
+        let strips = core::mem::take(&mut self.strips);
+        let alphas = core::mem::take(&mut self.alphas);
+        let strip_start_indices = core::mem::take(&mut self.strip_start_indices);
+        (strips, alphas, strip_start_indices)
+    }
 }
 
 /// A recording of rendering commands that can cache generated strips.
@@ -168,6 +176,11 @@
         (self.cached_strips.strips(), self.cached_strips.alphas())
     }
 
+    /// Takes cached strip buffers.
+    pub fn take_cached_strips(&mut self) -> (Vec<Strip>, Vec<u8>, Vec<usize>) {
+        self.cached_strips.take()
+    }
+
     /// Get strip start indices.
     pub fn get_strip_start_indices(&self) -> &[usize] {
         self.cached_strips.strip_start_indices()
@@ -206,14 +219,11 @@
 ///
 /// # State Modification During Replay
 ///
-/// **Important:** When replaying recordings using methods like `render_recording()`,
+/// **Important:** When replaying recordings using methods like `execute_recording()`,
 /// the renderer's state (transform, paint, fill rule, stroke settings, etc.) will be
 /// modified to match the state changes captured in the recording. The renderer will
 /// be left in the final state after all commands have been executed.
 ///
-/// If you need to preserve the original renderer state, save it before replaying
-/// recordings and restore it afterward.
-///
 /// # Multithreading Limitation
 ///
 /// **Note:** Recording and replay functionality is not currently implemented for
@@ -227,17 +237,18 @@
 /// ```ignore
 /// let mut recording = Recording::new();
 /// scene.record(&mut recording, |ctx| { ... });
-/// scene.render_recording(&mut recording);
+/// scene.prepare_recording(&mut recording);
+/// scene.execute_recording(&recording);
 /// ```
 ///
-/// And the following to prepare for later rendering:
+/// Or to prepare for later rendering:
 /// ```ignore
 /// let mut recording = Recording::new();
 /// scene.record(&mut recording, |ctx| { ... });
 /// scene.prepare_recording(&mut recording);
 ///
 /// // sometime later
-/// scene.render_recording(&mut recording);
+/// scene.execute_recording(&recording);
 /// ```
 pub trait Recordable {
     /// Record rendering commands into a recording.
@@ -280,21 +291,29 @@
     /// ```
     fn prepare_recording(&mut self, recording: &mut Recording);
 
-    /// Render using a recording (caches strips on first use).
+    /// Execute a recording directly without preparation.
     ///
-    /// This method executes a previously recorded sequence of operations.
-    /// On first use, it will generate and cache the necessary rendering data.
-    /// Subsequent calls will reuse the cached data for better performance.
-    fn render_recording(&mut self, recording: &mut Recording) {
-        self.prepare_recording(recording);
-        self.execute_recording(recording);
-    }
-
-    /// Execute a recording.
+    /// This method executes the rendering commands from a recording, using any
+    /// cached sparse strips that have been previously generated. If the recording
+    /// has not been prepared (no cached strips), this will result in empty rendering.
     ///
-    /// This method executes a previously recorded sequence of operations.
-    /// It will generate and cache the necessary rendering data if it hasn't been done yet.
-    /// Subsequent calls will reuse the cached data for better performance.
+    /// Use this method when you have a recording that has already been prepared
+    /// via `prepare_recording()`, or when you want to execute commands immediately
+    /// without explicit preparation.
+    ///
+    /// # Example
+    /// ```ignore
+    /// let mut recording = Recording::new();
+    /// scene.record(&mut recording, |ctx| {
+    ///     ctx.fill_rect(&Rect::new(0.0, 0.0, 100.0, 100.0));
+    /// });
+    ///
+    /// // Prepare strips first
+    /// scene.prepare_recording(&mut recording);
+    ///
+    /// // Then execute with cached strips
+    /// scene.execute_recording(&recording);
+    /// ```
     fn execute_recording(&mut self, recording: &Recording);
 }
 
diff --git a/sparse_strips/vello_cpu/src/dispatch/mod.rs b/sparse_strips/vello_cpu/src/dispatch/mod.rs
index 109fef7..1ee0850 100644
--- a/sparse_strips/vello_cpu/src/dispatch/mod.rs
+++ b/sparse_strips/vello_cpu/src/dispatch/mod.rs
@@ -57,6 +57,6 @@
     );
     fn alpha_buf(&self) -> &[u8];
     fn extend_alpha_buf(&mut self, alphas: &[u8]);
-    fn take_alpha_buf(&mut self) -> Vec<u8>;
+    fn replace_alpha_buf(&mut self, alphas: Vec<u8>) -> Vec<u8>;
     fn set_alpha_buf(&mut self, alphas: Vec<u8>);
 }
diff --git a/sparse_strips/vello_cpu/src/dispatch/multi_threaded.rs b/sparse_strips/vello_cpu/src/dispatch/multi_threaded.rs
index d8c8e01..726bf10 100644
--- a/sparse_strips/vello_cpu/src/dispatch/multi_threaded.rs
+++ b/sparse_strips/vello_cpu/src/dispatch/multi_threaded.rs
@@ -373,8 +373,8 @@
         unimplemented!("extend_alpha_buf is not implemented for multi-threaded dispatcher")
     }
 
-    fn take_alpha_buf(&mut self) -> Vec<u8> {
-        unimplemented!("take_alpha_buf is not implemented for multi-threaded dispatcher")
+    fn replace_alpha_buf(&mut self, _alphas: Vec<u8>) -> Vec<u8> {
+        unimplemented!("replace_alpha_buf is not implemented for multi-threaded dispatcher")
     }
 
     fn set_alpha_buf(&mut self, _alphas: Vec<u8>) {
diff --git a/sparse_strips/vello_cpu/src/dispatch/multi_threaded/worker.rs b/sparse_strips/vello_cpu/src/dispatch/multi_threaded/worker.rs
index 394eede..fce1a3e 100644
--- a/sparse_strips/vello_cpu/src/dispatch/multi_threaded/worker.rs
+++ b/sparse_strips/vello_cpu/src/dispatch/multi_threaded/worker.rs
@@ -157,6 +157,6 @@
     }
 
     pub(crate) fn finalize(&mut self) -> Vec<u8> {
-        self.strip_generator.take_alpha_buf()
+        self.strip_generator.replace_alpha_buf(Vec::new())
     }
 }
diff --git a/sparse_strips/vello_cpu/src/dispatch/single_threaded.rs b/sparse_strips/vello_cpu/src/dispatch/single_threaded.rs
index d040c93..42fb617 100644
--- a/sparse_strips/vello_cpu/src/dispatch/single_threaded.rs
+++ b/sparse_strips/vello_cpu/src/dispatch/single_threaded.rs
@@ -109,8 +109,8 @@
         self.strip_generator.extend_alpha_buf(alphas);
     }
 
-    fn take_alpha_buf(&mut self) -> Vec<u8> {
-        self.strip_generator.take_alpha_buf()
+    fn replace_alpha_buf(&mut self, alphas: Vec<u8>) -> Vec<u8> {
+        self.strip_generator.replace_alpha_buf(alphas)
     }
 
     fn set_alpha_buf(&mut self, alphas: Vec<u8>) {
diff --git a/sparse_strips/vello_cpu/src/render.rs b/sparse_strips/vello_cpu/src/render.rs
index ac96ca4..b1d3fd3 100644
--- a/sparse_strips/vello_cpu/src/render.rs
+++ b/sparse_strips/vello_cpu/src/render.rs
@@ -601,8 +601,9 @@
 
 impl Recordable for RenderContext {
     fn prepare_recording(&mut self, recording: &mut Recording) {
+        let buffers = recording.take_cached_strips();
         let (strips, alphas, strip_start_indices) =
-            self.generate_strips_from_commands(recording.commands());
+            self.generate_strips_from_commands(recording.commands(), buffers);
         recording.set_cached_strips(strips, alphas, strip_start_indices);
     }
 
@@ -707,12 +708,15 @@
     fn generate_strips_from_commands(
         &mut self,
         commands: &[RenderCommand],
+        buffers: (Vec<Strip>, Vec<u8>, Vec<usize>),
     ) -> (Vec<Strip>, Vec<u8>, Vec<usize>) {
-        let saved_state = self.take_current_state();
+        let (mut collected_strips, mut cached_alphas, mut strip_start_indices) = buffers;
+        collected_strips.clear();
+        cached_alphas.clear();
+        strip_start_indices.clear();
 
+        let saved_state = self.take_current_state(cached_alphas);
         let mut strip_generator = StripGenerator::new(self.width, self.height, self.level);
-        let mut collected_strips = Vec::new();
-        let mut strip_start_indices = Vec::new();
 
         for command in commands {
             let start_index = collected_strips.len();
@@ -813,14 +817,14 @@
     }
 
     /// Save the current rendering state.
-    fn take_current_state(&mut self) -> RenderState {
+    fn take_current_state(&mut self, alphas: Vec<u8>) -> RenderState {
         RenderState {
             paint: self.paint.clone(),
             paint_transform: self.paint_transform,
             transform: self.transform,
             fill_rule: self.fill_rule,
             stroke: core::mem::take(&mut self.stroke),
-            alphas: self.dispatcher.take_alpha_buf(),
+            alphas: self.dispatcher.replace_alpha_buf(alphas),
         }
     }
 
diff --git a/sparse_strips/vello_cpu/src/strip_generator.rs b/sparse_strips/vello_cpu/src/strip_generator.rs
index 4debfb4..a1fcd2a 100644
--- a/sparse_strips/vello_cpu/src/strip_generator.rs
+++ b/sparse_strips/vello_cpu/src/strip_generator.rs
@@ -91,6 +91,10 @@
         core::mem::take(&mut self.alphas)
     }
 
+    pub(crate) fn replace_alpha_buf(&mut self, alphas: Vec<u8>) -> Vec<u8> {
+        core::mem::replace(&mut self.alphas, alphas)
+    }
+
     pub(crate) fn reset(&mut self) {
         self.line_buf.clear();
         self.tiles.reset();
diff --git a/sparse_strips/vello_hybrid/examples/scenes/src/svg.rs b/sparse_strips/vello_hybrid/examples/scenes/src/svg.rs
index cb73a1a..124bf69 100644
--- a/sparse_strips/vello_hybrid/examples/scenes/src/svg.rs
+++ b/sparse_strips/vello_hybrid/examples/scenes/src/svg.rs
@@ -82,7 +82,7 @@
 
     // Case 1: Identical transforms - can reuse directly
     if transforms_are_identical(recording.transform(), current_transform) {
-        scene.render_recording(recording);
+        scene.execute_recording(recording);
         print_render_stats("Identical ", start.elapsed(), recording);
         return RenderResult { is_reused: true };
     }
@@ -100,7 +100,8 @@
     scene.record(&mut new_recording, |recorder| {
         render_svg_record(recorder, &scene_obj.svg.items, current_transform);
     });
-    scene.render_recording(&mut new_recording);
+    scene.prepare_recording(&mut new_recording);
+    scene.execute_recording(&new_recording);
     new_recording.set_transform(current_transform);
     print_render_stats("Fresh     ", start.elapsed(), &new_recording);
 
diff --git a/sparse_strips/vello_hybrid/src/scene.rs b/sparse_strips/vello_hybrid/src/scene.rs
index 700a9e3..7ac5c60 100644
--- a/sparse_strips/vello_hybrid/src/scene.rs
+++ b/sparse_strips/vello_hybrid/src/scene.rs
@@ -375,8 +375,9 @@
 
 impl Recordable for Scene {
     fn prepare_recording(&mut self, recording: &mut Recording) {
+        let buffers = recording.take_cached_strips();
         let (strips, alphas, strip_start_indices) =
-            self.generate_strips_from_commands(recording.commands());
+            self.generate_strips_from_commands(recording.commands(), buffers);
         recording.set_cached_strips(strips, alphas, strip_start_indices);
     }
 
@@ -466,11 +467,14 @@
     fn generate_strips_from_commands(
         &mut self,
         commands: &[RenderCommand],
+        buffers: (Vec<Strip>, Vec<u8>, Vec<usize>),
     ) -> (Vec<Strip>, Vec<u8>, Vec<usize>) {
-        let saved_state = self.take_current_state();
+        let (mut collected_strips, mut cached_alphas, mut strip_start_indices) = buffers;
+        collected_strips.clear();
+        cached_alphas.clear();
+        strip_start_indices.clear();
 
-        let mut collected_strips = Vec::new();
-        let mut strip_start_indices = Vec::new();
+        let saved_state = self.take_current_state(cached_alphas);
 
         for command in commands {
             let start_index = collected_strips.len();
@@ -507,10 +511,10 @@
             }
         }
 
-        let alphas = core::mem::take(&mut self.alphas);
+        let collected_alphas = core::mem::take(&mut self.alphas);
         self.restore_state(saved_state);
 
-        (collected_strips, alphas, strip_start_indices)
+        (collected_strips, collected_alphas, strip_start_indices)
     }
 
     /// Prepare cached strips for rendering by adjusting alpha indices and extending alpha buffer.
@@ -570,7 +574,7 @@
     }
 
     /// Save current rendering state.
-    fn take_current_state(&mut self) -> RenderState {
+    fn take_current_state(&mut self, cached_alphas: Vec<u8>) -> RenderState {
         RenderState {
             paint: self.paint.clone(),
             paint_transform: self.paint_transform,
@@ -579,7 +583,7 @@
             blend_mode: self.blend_mode,
             stroke: core::mem::take(&mut self.stroke),
             strip_buf: core::mem::take(&mut self.strip_buf),
-            alphas: core::mem::take(&mut self.alphas),
+            alphas: core::mem::replace(&mut self.alphas, cached_alphas),
         }
     }
 
diff --git a/sparse_strips/vello_sparse_tests/snapshots/recording_incremental_build.png b/sparse_strips/vello_sparse_tests/snapshots/recording_incremental_build.png
index ab97057..5e0f1d7 100644
--- a/sparse_strips/vello_sparse_tests/snapshots/recording_incremental_build.png
+++ b/sparse_strips/vello_sparse_tests/snapshots/recording_incremental_build.png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:a993966cca4c8b4ac88d1c3766e1c679dab24dc92e780f357c1b6635bc657c8d
-size 175
+oid sha256:fd33c91b2b8a16a61bbcb8461ae5fb3d10ff8d26d4915125fad3c2b9aa9c532b
+size 233
diff --git a/sparse_strips/vello_sparse_tests/tests/recording.rs b/sparse_strips/vello_sparse_tests/tests/recording.rs
index 28138c8..034f22e 100644
--- a/sparse_strips/vello_sparse_tests/tests/recording.rs
+++ b/sparse_strips/vello_sparse_tests/tests/recording.rs
@@ -33,14 +33,16 @@
         ctx.fill_rect(&Rect::new(92.0, 12.0, 96.0, 88.0));
     });
 
-    ctx.render_recording(&mut recording1);
+    ctx.prepare_recording(&mut recording1);
+    ctx.execute_recording(&recording1);
 
     ctx.set_paint(DARK_TURQUOISE);
     ctx.fill_rect(&Rect::new(12.0, 4.0, 88.0, 8.0));
     ctx.set_paint(LIGHT_SALMON);
     ctx.fill_rect(&Rect::new(12.0, 92.0, 88.0, 96.0));
 
-    ctx.render_recording(&mut recording2);
+    ctx.prepare_recording(&mut recording2);
+    ctx.execute_recording(&recording2);
 }
 
 #[vello_test(skip_multithreaded)]
diff --git a/sparse_strips/vello_sparse_tests/tests/renderer.rs b/sparse_strips/vello_sparse_tests/tests/renderer.rs
index 97e0ac8..515c0e6 100644
--- a/sparse_strips/vello_sparse_tests/tests/renderer.rs
+++ b/sparse_strips/vello_sparse_tests/tests/renderer.rs
@@ -52,7 +52,6 @@
     fn record(&mut self, recording: &mut Recording, f: impl FnOnce(&mut Recorder<'_>));
     fn prepare_recording(&mut self, recording: &mut Recording);
     fn execute_recording(&mut self, recording: &Recording);
-    fn render_recording(&mut self, recording: &mut Recording);
 }
 
 impl Renderer for RenderContext {
@@ -173,10 +172,6 @@
     fn execute_recording(&mut self, recording: &Recording) {
         Recordable::execute_recording(self, recording);
     }
-
-    fn render_recording(&mut self, recording: &mut Recording) {
-        Recordable::render_recording(self, recording);
-    }
 }
 
 #[cfg(not(all(target_arch = "wasm32", feature = "webgl")))]
@@ -484,10 +479,6 @@
     fn execute_recording(&mut self, recording: &Recording) {
         self.scene.execute_recording(recording);
     }
-
-    fn render_recording(&mut self, recording: &mut Recording) {
-        self.scene.render_recording(recording);
-    }
 }
 
 #[cfg(all(target_arch = "wasm32", feature = "webgl"))]
@@ -685,10 +676,6 @@
     fn execute_recording(&mut self, recording: &Recording) {
         self.scene.execute_recording(recording);
     }
-
-    fn render_recording(&mut self, recording: &mut Recording) {
-        self.scene.render_recording(recording);
-    }
 }
 
 impl GlyphRenderer for HybridRenderer {