diff --git a/book/src/chapter_1.md b/book/src/chapter_1.md
index 1f8a6da488463aa976d3d5774148a86559a38760..2842b931a3b1de350b2b8ace89d5b343c89ef8ef 100644
--- a/book/src/chapter_1.md
+++ b/book/src/chapter_1.md
@@ -19,7 +19,7 @@ fn startup(
 ) {
     font_mapping.set_default(asset_server.load("roboto.kayak_font"));
 
-    let mut widget_context = KayakRootContext::new();
+    let mut widget_context = KayakRootContext::new(camera_entity);
     let parent_id = None;
 
     // The rsx! macro expects a parent_id, a widget_context from the user.
@@ -37,7 +37,7 @@ fn startup(
         </KayakAppBundle>
     }
     
-    commands.spawn(UICameraBundle::new(widget_context));
+    commands.spawn((widget_context, EventDispatcher::default()));
 }
 
 fn main() {
@@ -63,9 +63,13 @@ fn startup(
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
+    let camera_entity = commands
+        .spawn((Camera2dBundle::default(), CameraUIKayak))
+        .id();
+
     font_mapping.set_default(asset_server.load("roboto.kayak_font"));
     commands.spawn(UICameraBundle::new());
-    let mut widget_context = KayakRootContext::new();
+    let mut widget_context = KayakRootContext::new(camera_entity);
 
     let app_entity = widget_context.spawn_widget(&mut commands, None);
     // Create default app bundle
@@ -95,9 +99,8 @@ fn startup(
     // Add app widget to context.
     widget_context.add_widget(None, app_entity);
 
-    // Add widget context as resource.
-    
- commands.spawn(UICameraBundle::new(widget_context));
+    // Spawn context as an entity.
+    commands.spawn((widget_context, EventDispatcher::default()));
 }
 fn main() {
     App::new()
diff --git a/examples/bevy_scene.rs b/examples/bevy_scene.rs
index bf8439b69e0f8639de094acb0df5cb2941d37c06..edea05e8e5c7c1ae6b6a5d3f9881a1ec5d07edd3 100644
--- a/examples/bevy_scene.rs
+++ b/examples/bevy_scene.rs
@@ -115,7 +115,6 @@ fn on_color_change(
 
 /// A system that sets up the world
 fn world_setup(mut commands: Commands, active_color: Res<ActiveColor>) {
-    commands.spawn((Camera2dBundle::default(), WorldCamera));
     commands
         .spawn(SpriteBundle {
             sprite: Sprite {
@@ -172,9 +171,15 @@ fn startup(
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
+    // The UI Camera and the world camera are the same.
+    // CameraUIKayak is used to tell kayak which camera should render UI.
+    let camera_entity = commands
+        .spawn((Camera2dBundle::default(), CameraUIKayak, WorldCamera))
+        .id();
+
     font_mapping.set_default(asset_server.load("roboto.kayak_font"));
 
-    let mut widget_context = KayakRootContext::new();
+    let mut widget_context = KayakRootContext::new(camera_entity);
     widget_context.add_plugin(KayakWidgetsContextPlugin);
 
     let handle_change_color = OnEvent::new(
@@ -255,7 +260,7 @@ fn startup(
         </KayakAppBundle>
     };
 
-    commands.spawn((UICameraBundle::new(widget_context), GameUI));
+    commands.spawn((widget_context, EventDispatcher::default(), GameUI));
 }
 
 fn main() {
diff --git a/examples/clipping.rs b/examples/clipping.rs
index 9eeb05889354fa863a2d7b63766892ccd9a29d31..36203347a6d4db0e83e6327e7f172fa7f84489c9 100644
--- a/examples/clipping.rs
+++ b/examples/clipping.rs
@@ -6,11 +6,15 @@ fn startup(
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
+    let camera_entity = commands
+        .spawn((Camera2dBundle::default(), CameraUIKayak))
+        .id();
+
     font_mapping.set_default(asset_server.load("lato-light.kttf"));
 
     let image = asset_server.load("panel.png");
 
-    let mut widget_context = KayakRootContext::new();
+    let mut widget_context = KayakRootContext::new(camera_entity);
     widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
 
@@ -56,7 +60,7 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sed tellus neque.
         </KayakAppBundle>
     };
 
-    commands.spawn(UICameraBundle::new(widget_context));
+    commands.spawn((widget_context, EventDispatcher::default()));
 }
 
 fn main() {
diff --git a/examples/conditional_widget.rs b/examples/conditional_widget.rs
index 1791f087cf45762443f3032586ee3d1355cb1c98..ca5b24bd7b0802f39e594ce519f181d5cbf422dc 100644
--- a/examples/conditional_widget.rs
+++ b/examples/conditional_widget.rs
@@ -109,13 +109,13 @@ fn startup(
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
-    font_mapping.set_default(asset_server.load("lato-light.kttf"));
+    let camera_entity = commands
+        .spawn((Camera2dBundle::default(), CameraUIKayak))
+        .id();
 
-    // Camera 2D forces a clear pass in bevy.
-    // We do this because our scene is not rendering anything else.
-    commands.spawn(Camera2dBundle::default());
+    font_mapping.set_default(asset_server.load("lato-light.kttf"));
 
-    let mut widget_context = KayakRootContext::new();
+    let mut widget_context = KayakRootContext::new(camera_entity);
     widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
     widget_context.add_widget_data::<MyWidget, MyWidgetState>();
@@ -130,7 +130,7 @@ fn startup(
         </KayakAppBundle>
     };
 
-    commands.spawn(UICameraBundle::new(widget_context));
+    commands.spawn((widget_context, EventDispatcher::default()));
 }
 
 fn main() {
diff --git a/examples/context.rs b/examples/context.rs
index a712519772efeb7726587817b1727e9256014ac3..b27dd7487352aa9ebdf226208fda898c6f139d23 100644
--- a/examples/context.rs
+++ b/examples/context.rs
@@ -325,13 +325,13 @@ fn startup(
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
-    font_mapping.set_default(asset_server.load("roboto.kayak_font"));
+    let camera_entity = commands
+        .spawn((Camera2dBundle::default(), CameraUIKayak))
+        .id();
 
-    // Camera 2D forces a clear pass in bevy.
-    // We do this because our scene is not rendering anything else.
-    commands.spawn(Camera2dBundle::default());
+    font_mapping.set_default(asset_server.load("roboto.kayak_font"));
 
-    let mut widget_context = KayakRootContext::new();
+    let mut widget_context = KayakRootContext::new(camera_entity);
     widget_context.add_plugin(KayakWidgetsContextPlugin);
     widget_context.add_widget_data::<ThemeDemo, EmptyState>();
     widget_context.add_widget_data::<ThemeButton, EmptyState>();
@@ -377,7 +377,7 @@ fn startup(
         </KayakAppBundle>
     };
 
-    commands.spawn(UICameraBundle::new(widget_context));
+    commands.spawn((widget_context, EventDispatcher::default()));
 }
 
 fn main() {
diff --git a/examples/demo.rs b/examples/demo.rs
index 0ba501e2ec1b438fcfab777ade91007f1ae7384e..0d562a49bf41bc761f641ea04122782a6d4da336 100644
--- a/examples/demo.rs
+++ b/examples/demo.rs
@@ -21,7 +21,11 @@ fn my_widget_1_render(
 impl Widget for MyWidget {}
 
 fn startup(mut commands: Commands) {
-    let mut context = KayakRootContext::new();
+    let camera_entity = commands
+        .spawn((Camera2dBundle::default(), CameraUIKayak))
+        .id();
+
+    let mut context = KayakRootContext::new(camera_entity);
     context.add_plugin(KayakWidgetsContextPlugin);
     context.add_widget_system(
         MyWidget::default().get_name(),
@@ -47,7 +51,7 @@ fn startup(mut commands: Commands) {
     });
     context.add_widget(None, app_entity);
 
-    commands.spawn(UICameraBundle::new(context));
+    commands.spawn((context, EventDispatcher::default()));
 }
 
 // Note this example shows prop changing not state changing which is quite different.
diff --git a/examples/hello_world.rs b/examples/hello_world.rs
index caf0660553c22cf932aa72ebfb63fcb1eafefffc..188504eb9d5bc0316036b79bbd7c15951b9175c8 100644
--- a/examples/hello_world.rs
+++ b/examples/hello_world.rs
@@ -1,14 +1,23 @@
 use bevy::prelude::*;
-use kayak_ui::prelude::{widgets::*, *};
+use kayak_ui::{
+    prelude::{widgets::*, *},
+    CameraUIKayak,
+};
 
 fn startup(
     mut commands: Commands,
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
+    let camera_entity = commands
+        .spawn(Camera2dBundle::default())
+        .insert(CameraUIKayak)
+        .id();
+
     font_mapping.set_default(asset_server.load("roboto.kayak_font"));
+    // font_mapping.force_subpixel(&asset_server.load("roboto.kayak_font"));
 
-    let mut widget_context = KayakRootContext::new();
+    let mut widget_context = KayakRootContext::new(camera_entity);
     widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
     rsx! {
@@ -23,7 +32,7 @@ fn startup(
         </KayakAppBundle>
     };
 
-    commands.spawn(UICameraBundle::new(widget_context));
+    commands.spawn((widget_context, EventDispatcher::default()));
 }
 
 fn main() {
diff --git a/examples/hello_world_no_macro.rs b/examples/hello_world_no_macro.rs
index 1b472f8eb962ed39ece9044244278655b1d0005b..7216668abb4043f7a24c896ab08fc824fb5149b2 100644
--- a/examples/hello_world_no_macro.rs
+++ b/examples/hello_world_no_macro.rs
@@ -5,8 +5,13 @@ fn startup(
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
+    let camera_entity = commands
+        .spawn(Camera2dBundle::default())
+        .insert(CameraUIKayak)
+        .id();
+
     font_mapping.set_default(asset_server.load("roboto.kayak_font"));
-    let mut widget_context = KayakRootContext::new();
+    let mut widget_context = KayakRootContext::new(camera_entity);
     widget_context.add_plugin(KayakWidgetsContextPlugin);
 
     let app_entity = widget_context.spawn_widget(&mut commands, None);
@@ -39,7 +44,7 @@ fn startup(
 
     // Add widget context as resource.
 
-    commands.spawn(UICameraBundle::new(widget_context));
+    commands.spawn((widget_context, EventDispatcher::default()));
 }
 fn main() {
     App::new()
diff --git a/examples/image.rs b/examples/image.rs
index ae984f6ebe19cee77666e543b6127fd89889414d..40c7edba142927b072af1cfeaf7e3963e994ba5b 100644
--- a/examples/image.rs
+++ b/examples/image.rs
@@ -6,11 +6,15 @@ fn startup(
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
+    let camera_entity = commands
+        .spawn((Camera2dBundle::default(), CameraUIKayak))
+        .id();
+
     font_mapping.set_default(asset_server.load("roboto.kayak_font"));
 
     let image = asset_server.load("generic-rpg-vendor.png");
 
-    let mut widget_context = KayakRootContext::new();
+    let mut widget_context = KayakRootContext::new(camera_entity);
     widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
     rsx! {
@@ -30,7 +34,7 @@ fn startup(
         </KayakAppBundle>
     };
 
-    commands.spawn(UICameraBundle::new(widget_context));
+    commands.spawn((widget_context, EventDispatcher::default()));
 }
 
 fn main() {
diff --git a/examples/layout.rs b/examples/layout.rs
index 6fdc489262656d4c4e1dc796f472809824ce5572..f2a0e842befff80835b0279686ac37c149352099 100644
--- a/examples/layout.rs
+++ b/examples/layout.rs
@@ -6,13 +6,13 @@ fn startup(
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
-    font_mapping.set_default(asset_server.load("roboto.kayak_font"));
+    let camera_entity = commands
+        .spawn((Camera2dBundle::default(), CameraUIKayak))
+        .id();
 
-    // Camera 2D forces a clear pass in bevy.
-    // We do this because our scene is not rendering anything else.
-    commands.spawn(Camera2dBundle::default());
+    font_mapping.set_default(asset_server.load("roboto.kayak_font"));
 
-    let mut widget_context = KayakRootContext::new();
+    let mut widget_context = KayakRootContext::new(camera_entity);
     widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
 
@@ -130,7 +130,7 @@ fn startup(
         </KayakAppBundle>
     };
 
-    commands.spawn(UICameraBundle::new(widget_context));
+    commands.spawn((widget_context, EventDispatcher::default()));
 }
 
 fn main() {
diff --git a/examples/main_menu.rs b/examples/main_menu.rs
index 40949d683ca0689499637a650580f8fef8e79df8..a66ab6bdd980479f843914c7824f5a94359f6134 100644
--- a/examples/main_menu.rs
+++ b/examples/main_menu.rs
@@ -123,9 +123,13 @@ fn startup(
     asset_server: Res<AssetServer>,
     mut preload_resource: ResMut<PreloadResource>,
 ) {
+    let camera_entity = commands
+        .spawn((Camera2dBundle::default(), CameraUIKayak))
+        .id();
+
     font_mapping.set_default(asset_server.load("lato-light.kttf"));
 
-    let mut widget_context = KayakRootContext::new();
+    let mut widget_context = KayakRootContext::new(camera_entity);
     widget_context.add_plugin(KayakWidgetsContextPlugin);
     widget_context.add_widget_data::<MenuButton, ButtonState>();
     widget_context.add_widget_system(
@@ -218,7 +222,7 @@ fn startup(
         </KayakAppBundle>
     };
 
-    commands.spawn(UICameraBundle::new(widget_context));
+    commands.spawn((widget_context, EventDispatcher::default()));
 }
 
 fn main() {
diff --git a/examples/multi_context.rs b/examples/multi_context.rs
deleted file mode 100644
index 8b137891791fe96927ad78e64b0aad7bded08bdc..0000000000000000000000000000000000000000
--- a/examples/multi_context.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/examples/nine_patch.rs b/examples/nine_patch.rs
index 1aeeadaa5adaf3abacb88cfa9ebdf8ea3f856847..ea211a725baacca64ccb2df3f49c97496d85a64a 100644
--- a/examples/nine_patch.rs
+++ b/examples/nine_patch.rs
@@ -6,11 +6,15 @@ fn startup(
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
+    let camera_entity = commands
+        .spawn((Camera2dBundle::default(), CameraUIKayak))
+        .id();
+
     font_mapping.set_default(asset_server.load("roboto.kayak_font"));
 
     let image = asset_server.load("panel.png");
 
-    let mut widget_context = KayakRootContext::new();
+    let mut widget_context = KayakRootContext::new(camera_entity);
     widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
 
@@ -51,7 +55,7 @@ fn startup(
         </KayakAppBundle>
     };
 
-    commands.spawn(UICameraBundle::new(widget_context));
+    commands.spawn((widget_context, EventDispatcher::default()));
 }
 
 fn main() {
diff --git a/examples/quads.rs b/examples/quads.rs
index 33216d627d9568f50e24debf88b7025ce2b9c690..6271d2602c056bf64a3799d56d3b3802e9c8caec 100644
--- a/examples/quads.rs
+++ b/examples/quads.rs
@@ -94,13 +94,13 @@ fn startup(
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
-    font_mapping.set_default(asset_server.load("roboto.kayak_font"));
+    let camera_entity = commands
+        .spawn((Camera2dBundle::default(), CameraUIKayak))
+        .id();
 
-    // Camera 2D forces a clear pass in bevy.
-    // We do this because our scene is not rendering anything else.
-    commands.spawn(Camera2dBundle::default());
+    font_mapping.set_default(asset_server.load("roboto.kayak_font"));
 
-    let mut widget_context = KayakRootContext::new();
+    let mut widget_context = KayakRootContext::new(camera_entity);
     widget_context.add_plugin(KayakWidgetsContextPlugin);
     widget_context.add_widget_system(
         MyQuad::default().get_name(),
@@ -134,7 +134,7 @@ fn startup(
         </KayakAppBundle>
     };
 
-    commands.spawn(UICameraBundle::new(widget_context));
+    commands.spawn((widget_context, EventDispatcher::default()));
 }
 
 fn main() {
diff --git a/examples/render_target.rs b/examples/render_target.rs
index d17bb3ba89908ac328ccd59f7c5ae8da88b5e3c3..3f8fc0dfbd0bc2e2443d9f95b82222854124cc7c 100644
--- a/examples/render_target.rs
+++ b/examples/render_target.rs
@@ -56,7 +56,22 @@ fn startup(
 
     let image_handle = images.add(image);
 
-    let mut widget_context = KayakRootContext::new();
+    let camera_entity = commands
+        .spawn(Camera2dBundle {
+            camera: Camera {
+                priority: -1,
+                target: RenderTarget::Image(image_handle.clone()),
+                ..Camera::default()
+            },
+            camera_2d: Camera2d {
+                clear_color: bevy::core_pipeline::clear_color::ClearColorConfig::Default,
+            },
+            ..Default::default()
+        })
+        .insert(CameraUIKayak)
+        .id();
+
+    let mut widget_context = KayakRootContext::new(camera_entity);
     widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
     rsx! {
@@ -81,18 +96,7 @@ fn startup(
             />
         </KayakAppBundle>
     };
-
-    commands.spawn(UICameraBundle {
-        camera: Camera {
-            priority: -1,
-            target: RenderTarget::Image(image_handle.clone()),
-            ..Camera::default()
-        },
-        camera_ui: CameraUIKayak {
-            clear_color: bevy::core_pipeline::clear_color::ClearColorConfig::Default,
-        },
-        ..UICameraBundle::new(widget_context)
-    });
+    commands.spawn((widget_context, EventDispatcher::default()));
 
     // Setup 3D scene
     // Light
@@ -127,14 +131,17 @@ fn startup(
         .insert(MainPassCube);
 
     // The main pass camera.
-    commands.spawn(Camera3dBundle {
-        transform: Transform::from_translation(Vec3::new(0.0, 0.0, 15.0))
-            .looking_at(Vec3::default(), Vec3::Y),
-        ..default()
-    });
+    let camera_entity = commands
+        .spawn(Camera3dBundle {
+            transform: Transform::from_translation(Vec3::new(0.0, 0.0, 15.0))
+                .looking_at(Vec3::default(), Vec3::Y),
+            ..default()
+        })
+        .insert(CameraUIKayak)
+        .id();
 
     // Spawn another UI in 2D space!
-    let mut widget_context = KayakRootContext::new();
+    let mut widget_context = KayakRootContext::new(camera_entity);
     widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
     rsx! {
@@ -148,7 +155,7 @@ fn startup(
             />
         </KayakAppBundle>
     };
-    commands.spawn((UICameraBundle::new(widget_context), MainUI));
+    commands.spawn((widget_context, EventDispatcher::default(), MainUI));
 }
 
 /// Rotates the outer cube (main pass)
diff --git a/examples/scrolling.rs b/examples/scrolling.rs
index cac9c08d53e7aae9a0a4b3ff770dea080a243241..d77fa08a432ae3675ba93899b6bd621863c8c9b1 100644
--- a/examples/scrolling.rs
+++ b/examples/scrolling.rs
@@ -6,6 +6,10 @@ fn startup(
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
+    let camera_entity = commands
+        .spawn((Camera2dBundle::default(), CameraUIKayak))
+        .id();
+
     let font_asset = asset_server.load("roboto.kayak_font");
     font_mapping.set_default(font_asset.clone());
 
@@ -14,11 +18,7 @@ fn startup(
     // will be ignored if this setting is used.
     font_mapping.force_subpixel(&font_asset);
 
-    // Camera 2D forces a clear pass in bevy.
-    // We do this because our scene is not rendering anything else.
-    commands.spawn(Camera2dBundle::default());
-
-    let mut widget_context = KayakRootContext::new();
+    let mut widget_context = KayakRootContext::new(camera_entity);
     widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
 
@@ -60,7 +60,7 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sed tellus neque.
         </KayakAppBundle>
     };
 
-    commands.spawn(UICameraBundle::new(widget_context));
+    commands.spawn((widget_context, EventDispatcher::default()));
 }
 
 fn main() {
diff --git a/examples/simple_state.rs b/examples/simple_state.rs
index 2e27896d14c468df56b2b3aae131db2160cbb8e2..05bd20867750ca29a2c1dd39d0941fe533f3ef0e 100644
--- a/examples/simple_state.rs
+++ b/examples/simple_state.rs
@@ -85,13 +85,13 @@ fn startup(
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
-    font_mapping.set_default(asset_server.load("lato-light.kttf"));
+    let camera_entity = commands
+        .spawn((Camera2dBundle::default(), CameraUIKayak))
+        .id();
 
-    // Camera 2D forces a clear pass in bevy.
-    // We do this because our scene is not rendering anything else.
-    commands.spawn(Camera2dBundle::default());
+    font_mapping.set_default(asset_server.load("lato-light.kttf"));
 
-    let mut widget_context = KayakRootContext::new();
+    let mut widget_context = KayakRootContext::new(camera_entity);
     widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
     widget_context.add_widget_data::<CurrentCount, CurrentCountState>();
@@ -129,7 +129,7 @@ fn startup(
         </KayakAppBundle>
     };
 
-    commands.spawn(UICameraBundle::new(widget_context));
+    commands.spawn((widget_context, EventDispatcher::default()));
 }
 
 fn main() {
diff --git a/examples/tabs/tabs.rs b/examples/tabs/tabs.rs
index 2539aecd9720908d149348c467003415284a2210..9ecb4b53a67c422678a65e0c1255a5cd25c1d3b8 100644
--- a/examples/tabs/tabs.rs
+++ b/examples/tabs/tabs.rs
@@ -15,13 +15,13 @@ fn startup(
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
-    font_mapping.set_default(asset_server.load("roboto.kayak_font"));
+    let camera_entity = commands
+        .spawn((Camera2dBundle::default(), CameraUIKayak))
+        .id();
 
-    // Camera 2D forces a clear pass in bevy.
-    // We do this because our scene is not rendering anything else.
-    commands.spawn(Camera2dBundle::default());
+    font_mapping.set_default(asset_server.load("roboto.kayak_font"));
 
-    let mut widget_context = KayakRootContext::new();
+    let mut widget_context = KayakRootContext::new(camera_entity);
     widget_context.add_plugin(KayakWidgetsContextPlugin);
     widget_context.add_widget_data::<Tab, EmptyState>();
     widget_context.add_widget_data::<TabContextProvider, EmptyState>();
@@ -87,7 +87,7 @@ fn startup(
         </KayakAppBundle>
     };
 
-    commands.spawn(UICameraBundle::new(widget_context));
+    commands.spawn((widget_context, EventDispatcher::default()));
 }
 
 fn main() {
diff --git a/examples/test_no_startup.rs b/examples/test_no_startup.rs
index 060081ee9ab9b6337605abc4f220d4ebcf4b9460..560628148103e204456e1320b90fa80e7ebd5a6b 100644
--- a/examples/test_no_startup.rs
+++ b/examples/test_no_startup.rs
@@ -17,9 +17,13 @@ fn second_sys(
     asset_server: Res<AssetServer>,
     mut font_mapping: ResMut<FontMapping>,
 ) {
+    let camera_entity = commands
+        .spawn((Camera2dBundle::default(), CameraUIKayak))
+        .id();
+
     font_mapping.set_default(asset_server.load("roboto.kayak_font"));
 
-    let mut widget_context = KayakRootContext::new();
+    let mut widget_context = KayakRootContext::new(camera_entity);
     widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
     rsx! {
@@ -34,7 +38,7 @@ fn second_sys(
         </KayakAppBundle>
     };
 
-    commands.spawn(UICameraBundle::new(widget_context));
+    commands.spawn((widget_context, EventDispatcher::default()));
 }
 
 fn main() {
diff --git a/examples/text.rs b/examples/text.rs
index fa49287c6f47b86904eb8466d09b5a13c6af626d..496023d6d5d3fc8ea978f5c25a687ae311360fe3 100644
--- a/examples/text.rs
+++ b/examples/text.rs
@@ -73,13 +73,13 @@ fn startup(
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
-    // Camera 2D forces a clear pass in bevy.
-    // We do this because our scene is not rendering anything else.
-    commands.spawn(Camera2dBundle::default());
+    let camera_entity = commands
+        .spawn((Camera2dBundle::default(), CameraUIKayak))
+        .id();
 
     font_mapping.set_default(asset_server.load("roboto.kayak_font"));
 
-    let mut widget_context = KayakRootContext::new();
+    let mut widget_context = KayakRootContext::new(camera_entity);
     widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
     widget_context.add_widget_data::<MyWidgetProps, EmptyState>();
@@ -92,7 +92,7 @@ fn startup(
         <KayakAppBundle><MyWidgetBundle props={MyWidgetProps { foo: 0 }} /></KayakAppBundle>
     };
 
-    commands.spawn(UICameraBundle::new(widget_context));
+    commands.spawn((widget_context, EventDispatcher::default()));
 }
 
 fn update_resource(keyboard_input: Res<Input<KeyCode>>, mut my_resource: ResMut<MyResource>) {
diff --git a/examples/text_box.rs b/examples/text_box.rs
index 72b273156600b77fc3f2ef3436bca5bb6a02d0b0..edb8e9f48deb69bf07f60bd2991ca9fd53a29ff2 100644
--- a/examples/text_box.rs
+++ b/examples/text_box.rs
@@ -88,13 +88,13 @@ fn startup(
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
-    font_mapping.set_default(asset_server.load("roboto.kayak_font"));
+    let camera_entity = commands
+        .spawn((Camera2dBundle::default(), CameraUIKayak))
+        .id();
 
-    // Camera 2D forces a clear pass in bevy.
-    // We do this because our scene is not rendering anything else.
-    commands.spawn(Camera2dBundle::default());
+    font_mapping.set_default(asset_server.load("roboto.kayak_font"));
 
-    let mut widget_context = KayakRootContext::new();
+    let mut widget_context = KayakRootContext::new(camera_entity);
     widget_context.add_plugin(KayakWidgetsContextPlugin);
 
     widget_context.add_widget_data::<TextBoxExample, TextBoxExampleState>();
@@ -120,7 +120,7 @@ fn startup(
         </KayakAppBundle>
     };
 
-    commands.spawn(UICameraBundle::new(widget_context));
+    commands.spawn((widget_context, EventDispatcher::default()));
 }
 
 fn main() {
diff --git a/examples/texture_atlas.rs b/examples/texture_atlas.rs
index d4e311ae9fa9825ef10fe6e3659aefc5d7239ff9..69f7d7d99ae1009bb6e87bed46684f40f0612fde 100644
--- a/examples/texture_atlas.rs
+++ b/examples/texture_atlas.rs
@@ -6,6 +6,10 @@ fn startup(
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
+    let camera_entity = commands
+        .spawn((Camera2dBundle::default(), CameraUIKayak))
+        .id();
+
     font_mapping.set_default(asset_server.load("roboto.kayak_font"));
 
     let image_handle = asset_server.load("texture_atlas.png");
@@ -28,7 +32,7 @@ fn startup(
     //The flower is in the 6(-1) row and 15 collumn
     let flower_index = columns * 5 + 15;
 
-    let mut widget_context = KayakRootContext::new();
+    let mut widget_context = KayakRootContext::new(camera_entity);
     widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
 
@@ -68,7 +72,7 @@ fn startup(
         </KayakAppBundle>
     };
 
-    commands.spawn(UICameraBundle::new(widget_context));
+    commands.spawn((widget_context, EventDispatcher::default()));
 }
 
 fn main() {
diff --git a/examples/todo/todo.rs b/examples/todo/todo.rs
index 4f62b8823c5794297a8394d45e1e86dc80e23152..c47178ffbbbb7faf7243d7e7f5950ce93c6e2aa1 100644
--- a/examples/todo/todo.rs
+++ b/examples/todo/todo.rs
@@ -53,13 +53,13 @@ fn startup(
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
-    font_mapping.set_default(asset_server.load("roboto.kayak_font"));
+    let camera_entity = commands
+        .spawn((Camera2dBundle::default(), CameraUIKayak))
+        .id();
 
-    // Camera 2D forces a clear pass in bevy.
-    // We do this because our scene is not rendering anything else.
-    commands.spawn(Camera2dBundle::default());
+    font_mapping.set_default(asset_server.load("roboto.kayak_font"));
 
-    let mut widget_context = KayakRootContext::new();
+    let mut widget_context = KayakRootContext::new(camera_entity);
     widget_context.add_plugin(KayakWidgetsContextPlugin);
     widget_context.add_widget_data::<TodoItemsProps, EmptyState>();
     widget_context.add_widget_data::<TodoInputProps, EmptyState>();
@@ -99,7 +99,7 @@ fn startup(
         </KayakAppBundle>
     };
 
-    commands.spawn(UICameraBundle::new(widget_context));
+    commands.spawn((widget_context, EventDispatcher::default()));
 }
 
 fn main() {
diff --git a/examples/vec.rs b/examples/vec.rs
index fa362a71ca9d2e76f780a8cbab998a538b3c030f..459e579371b2246ae5614c8253f969aa401ca12f 100644
--- a/examples/vec.rs
+++ b/examples/vec.rs
@@ -57,9 +57,13 @@ fn startup(
     mut font_mapping: ResMut<FontMapping>,
     asset_server: Res<AssetServer>,
 ) {
+    let camera_entity = commands
+        .spawn((Camera2dBundle::default(), CameraUIKayak))
+        .id();
+
     font_mapping.set_default(asset_server.load("roboto.kayak_font"));
 
-    let mut widget_context = KayakRootContext::new();
+    let mut widget_context = KayakRootContext::new(camera_entity);
     widget_context.add_plugin(KayakWidgetsContextPlugin);
     let parent_id = None;
     widget_context.add_widget_data::<MyWidgetProps, EmptyState>();
@@ -72,7 +76,7 @@ fn startup(
         <KayakAppBundle><MyWidgetBundle /></KayakAppBundle>
     };
 
-    commands.spawn(UICameraBundle::new(widget_context));
+    commands.spawn((widget_context, EventDispatcher::default()));
 }
 
 fn main() {
diff --git a/src/camera/camera.rs b/src/camera/camera.rs
deleted file mode 100644
index f432acedb4b8a8aa7a0ea9deb165dd981dcb0c70..0000000000000000000000000000000000000000
--- a/src/camera/camera.rs
+++ /dev/null
@@ -1,96 +0,0 @@
-use bevy::{
-    core_pipeline::clear_color::ClearColorConfig,
-    ecs::query::QueryItem,
-    prelude::{Bundle, Component, GlobalTransform, Transform, With},
-    render::{
-        camera::{Camera, CameraProjection, CameraRenderGraph, WindowOrigin},
-        extract_component::ExtractComponent,
-        primitives::Frustum,
-        view::VisibleEntities,
-    },
-};
-
-use crate::{context::KayakRootContext, event_dispatcher::EventDispatcher};
-
-use super::ortho::UIOrthographicProjection;
-
-/// Kayak UI's default UI camera.
-#[derive(Component, Clone, Default)]
-pub struct CameraUIKayak {
-    pub clear_color: ClearColorConfig,
-}
-
-impl ExtractComponent for CameraUIKayak {
-    type Query = &'static Self;
-    type Filter = With<Camera>;
-
-    fn extract_component(item: QueryItem<Self::Query>) -> Self {
-        item.clone()
-    }
-}
-
-/// Kayak UI's default UI camera bundle.
-#[derive(Bundle)]
-pub struct UICameraBundle {
-    pub camera: Camera,
-    // pub camera_2d: Camera2d,
-    pub camera_render_graph: CameraRenderGraph,
-    pub orthographic_projection: UIOrthographicProjection,
-    pub visible_entities: VisibleEntities,
-    pub frustum: Frustum,
-    pub transform: Transform,
-    pub global_transform: GlobalTransform,
-    pub camera_ui: CameraUIKayak,
-    pub context: KayakRootContext,
-    pub event_disaptcher: EventDispatcher,
-}
-
-impl Default for UICameraBundle {
-    fn default() -> Self {
-        Self::new(KayakRootContext::default())
-    }
-}
-
-impl UICameraBundle {
-    pub const UI_CAMERA: &'static str = "KAYAK_UI_CAMERA";
-    pub fn new(kayak_root_context: KayakRootContext) -> Self {
-        // we want 0 to be "closest" and +far to be "farthest" in 2d, so we offset
-        // the camera's translation by far and use a right handed coordinate system
-        let far = 1000.0;
-
-        let orthographic_projection = UIOrthographicProjection {
-            far,
-            window_origin: WindowOrigin::BottomLeft,
-            ..Default::default()
-        };
-
-        let transform = Transform::from_xyz(0.0, 0.0, far - 0.1);
-
-        let view_projection =
-            orthographic_projection.get_projection_matrix() * transform.compute_matrix().inverse();
-        let frustum = Frustum::from_view_projection(
-            &view_projection,
-            &transform.translation,
-            &transform.back(),
-            orthographic_projection.far(),
-        );
-        UICameraBundle {
-            camera: Camera {
-                priority: isize::MAX - 1,
-                ..Default::default()
-            },
-            camera_render_graph: CameraRenderGraph::new(bevy::core_pipeline::core_2d::graph::NAME),
-            orthographic_projection,
-            frustum,
-            visible_entities: VisibleEntities::default(),
-            transform,
-            // camera_2d: Camera2d::default(),
-            global_transform: Default::default(),
-            camera_ui: CameraUIKayak {
-                clear_color: ClearColorConfig::None,
-            },
-            context: kayak_root_context,
-            event_disaptcher: EventDispatcher::new(),
-        }
-    }
-}
diff --git a/src/camera/mod.rs b/src/camera/mod.rs
index 60e86c38d313c7d30e88d0dc490618246882de3e..0d5c7be1a84826ece668f57b2b0b925b88f7acda 100644
--- a/src/camera/mod.rs
+++ b/src/camera/mod.rs
@@ -1,23 +1,23 @@
-use bevy::{
-    prelude::{CoreStage, Plugin},
-    render::{camera::CameraProjectionPlugin, extract_component::ExtractComponentPlugin},
-};
-
-mod camera;
-mod ortho;
-
-pub use camera::{CameraUIKayak, UICameraBundle};
-pub(crate) use ortho::UIOrthographicProjection;
-
-pub struct KayakUICameraPlugin;
-
-impl Plugin for KayakUICameraPlugin {
-    fn build(&self, app: &mut bevy::prelude::App) {
-        app.add_system_to_stage(
-            CoreStage::PostUpdate,
-            bevy::render::camera::camera_system::<UIOrthographicProjection>,
-        )
-        .add_plugin(CameraProjectionPlugin::<UIOrthographicProjection>::default())
-        .add_plugin(ExtractComponentPlugin::<CameraUIKayak>::default());
-    }
-}
+use bevy::{
+    ecs::query::QueryItem,
+    prelude::*,
+    render::extract_component::{ExtractComponent, ExtractComponentPlugin},
+};
+
+#[derive(Component, Default, Debug, Clone, Copy)]
+pub struct CameraUIKayak;
+impl ExtractComponent for CameraUIKayak {
+    type Query = &'static Self;
+    type Filter = With<Camera>;
+
+    fn extract_component(item: QueryItem<Self::Query>) -> Self {
+        item.clone()
+    }
+}
+
+pub struct KayakUICameraPlugin;
+impl Plugin for KayakUICameraPlugin {
+    fn build(&self, app: &mut bevy::prelude::App) {
+        app.add_plugin(ExtractComponentPlugin::<CameraUIKayak>::default());
+    }
+}
diff --git a/src/camera/ortho.rs b/src/camera/ortho.rs
deleted file mode 100644
index bb7a800f9c75ac5010c0f9ee193d6fc8dbd578b4..0000000000000000000000000000000000000000
--- a/src/camera/ortho.rs
+++ /dev/null
@@ -1,72 +0,0 @@
-use bevy::ecs::reflect::ReflectComponent;
-use bevy::prelude::Component;
-use bevy::{
-    math::Mat4,
-    reflect::Reflect,
-    render::camera::{CameraProjection, ScalingMode, WindowOrigin},
-};
-
-/// Kayak UI's default orthographic projection matrix
-/// This matrix uses top left as 0, 0
-/// and bottom right as width, height.
-/// This projection layout is typical for most UI systems.
-#[derive(Debug, Clone, Component, Reflect)]
-#[reflect(Component)]
-pub struct UIOrthographicProjection {
-    pub left: f32,
-    pub right: f32,
-    pub bottom: f32,
-    pub top: f32,
-    pub near: f32,
-    pub far: f32,
-    pub window_origin: WindowOrigin,
-    pub scaling_mode: ScalingMode,
-    pub scale: f32,
-}
-
-impl CameraProjection for UIOrthographicProjection {
-    fn get_projection_matrix(&self) -> Mat4 {
-        Mat4::orthographic_rh(
-            self.left * self.scale,
-            self.right * self.scale,
-            self.bottom * self.scale,
-            self.top * self.scale,
-            // NOTE: near and far are swapped to invert the depth range from [0,1] to [1,0]
-            // This is for interoperability with pipelines using infinite reverse perspective projections.
-            self.far,
-            self.near,
-        )
-    }
-
-    fn update(&mut self, width: f32, height: f32) {
-        match (&self.scaling_mode, &self.window_origin) {
-            (ScalingMode::WindowSize, WindowOrigin::BottomLeft) => {
-                self.left = 0.0;
-                self.right = width;
-                self.top = 0.0;
-                self.bottom = height;
-            }
-            _ => {}
-        }
-    }
-
-    fn far(&self) -> f32 {
-        self.far
-    }
-}
-
-impl Default for UIOrthographicProjection {
-    fn default() -> Self {
-        UIOrthographicProjection {
-            left: -1.0,
-            right: 1.0,
-            bottom: -1.0,
-            top: 1.0,
-            near: 0.0,
-            far: 1000.0,
-            window_origin: WindowOrigin::Center,
-            scaling_mode: ScalingMode::WindowSize,
-            scale: 1.0,
-        }
-    }
-}
diff --git a/src/context.rs b/src/context.rs
index 653b035eb6d96b70dd5f4485cf7cff41ea29f040..03c1905ef4d77e7a8430561db1c328513a86561e 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -64,7 +64,7 @@ type WidgetSystems = HashMap<
 ///     }).id();
 ///     // Stores the kayak app widget in the widget context's tree.
 ///     widget_context.add_widget(None, app_entity);
-///     commands.spawn(UICameraBundle::new(widget_context));
+///     commands.spawn((widget_context, EventDispatcher::default()));
 /// }
 ///
 /// fn main() {
diff --git a/src/lib.rs b/src/lib.rs
index 3f478f47f4ddfcfc3ec46b1ce935af96c79e3d5b..02ac24264915c4f44ccfe4cbc0ab1f7a9f8ff887 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -38,7 +38,7 @@ pub use camera::*;
 pub const DEFAULT_FONT: &str = "Kayak-Default";
 
 pub mod prelude {
-    pub use crate::camera::UICameraBundle;
+    pub use crate::camera::*;
     pub use crate::children::KChildren;
     pub use crate::clone_component::PreviousWidget;
     pub use crate::context::*;
@@ -53,6 +53,7 @@ pub mod prelude {
     pub use crate::on_change::OnChange;
     pub use crate::on_event::OnEvent;
     pub use crate::on_layout::OnLayout;
+    pub use crate::render::draw_ui_graph;
     pub use crate::render::font::FontMapping;
     pub use crate::styles::*;
     pub use crate::tree::*;
@@ -62,7 +63,6 @@ pub mod prelude {
     pub use crate::widgets;
     pub use kayak_font::Alignment;
     pub use kayak_ui_macros::{constructor, rsx};
-    pub use crate::render::draw_ui_graph;
 }
 
 pub use focus_tree::Focusable;
diff --git a/src/render/extract.rs b/src/render/extract.rs
index 3b2a2f6103ceacc5c9a1bb92b0131390466142e3..8859960f16b3bbdc439e47a161f9835a955c4f46 100644
--- a/src/render/extract.rs
+++ b/src/render/extract.rs
@@ -2,12 +2,14 @@ use crate::{
     context::{KayakRootContext, WidgetName},
     node::Node,
     render_primitive::RenderPrimitive,
-    styles::Corner, CameraUIKayak,
+    styles::Corner,
+    CameraUIKayak,
 };
 use bevy::{
     prelude::*,
-    render::{Extract, RenderApp, RenderStage, view::ExtractedView, render_phase::RenderPhase},
-    window::Windows, ui::TransparentUi,
+    render::{render_phase::RenderPhase, view::ExtractedView, Extract, RenderApp, RenderStage},
+    ui::TransparentUi,
+    window::Windows,
 };
 use kayak_font::KayakFont;
 
@@ -26,8 +28,14 @@ impl Plugin for BevyKayakUIExtractPlugin {
     fn build(&self, app: &mut bevy::prelude::App) {
         let render_app = app.sub_app_mut(RenderApp);
         render_app.add_system_to_stage(RenderStage::Extract, extract);
-        render_app.add_system_to_stage(RenderStage::Extract, extract_default_ui_camera_view::<Camera2d>);
-        render_app.add_system_to_stage(RenderStage::Extract, extract_default_ui_camera_view::<Camera3d>);
+        render_app.add_system_to_stage(
+            RenderStage::Extract,
+            extract_default_ui_camera_view::<Camera2d>,
+        );
+        render_app.add_system_to_stage(
+            RenderStage::Extract,
+            extract_default_ui_camera_view::<Camera3d>,
+        );
     }
 }
 
@@ -59,7 +67,11 @@ pub fn extract(
             1.0
         };
         let mut new_render_primitives = context.build_render_primitives(&node_query, &widget_names);
-        render_primitives.extend(new_render_primitives.drain(..).map(|r| (context.camera_entity, dpi, r)));
+        render_primitives.extend(
+            new_render_primitives
+                .drain(..)
+                .map(|r| (context.camera_entity, dpi, r)),
+        );
     }
 
     let mut extracted_quads = Vec::new();
@@ -131,15 +143,13 @@ pub fn extract(
 #[derive(Component)]
 pub struct DefaultCameraView(pub Entity);
 
-
 const UI_CAMERA_TRANSFORM_OFFSET: f32 = -0.1;
 
 pub fn extract_default_ui_camera_view<T: Component>(
     mut commands: Commands,
     query: Extract<Query<(Entity, &Camera, &CameraUIKayak), With<T>>>,
 ) {
-    for (entity, camera, camera_ui) in &query {
-
+    for (entity, camera, _camera_ui) in &query {
         if let (Some(logical_size), Some((physical_origin, _)), Some(physical_size)) = (
             camera.logical_viewport_size(),
             camera.physical_viewport_rect(),
@@ -171,4 +181,4 @@ pub fn extract_default_ui_camera_view<T: Component>(
             ));
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/render/ui_pass.rs b/src/render/ui_pass.rs
index d9310bfdd146cab07b7c12369d471bb3b2b4217d..8873ce6042095a4688dd6dd638c2cfc6862d66a7 100644
--- a/src/render/ui_pass.rs
+++ b/src/render/ui_pass.rs
@@ -1,132 +1,130 @@
-use bevy::core_pipeline::clear_color::ClearColorConfig;
-use bevy::ecs::prelude::*;
-use bevy::prelude::ClearColor;
-use bevy::render::render_phase::{DrawFunctionId, PhaseItem};
-use bevy::render::render_resource::CachedRenderPipelineId;
-use bevy::render::{
-    render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
-    render_phase::{DrawFunctions, RenderPhase, TrackedRenderPass},
-    render_resource::{LoadOp, Operations, RenderPassDescriptor},
-    renderer::RenderContext,
-    view::{ExtractedView, ViewTarget},
-};
-use bevy::utils::FloatOrd;
-
-use crate::CameraUIKayak;
-
-use super::extract::DefaultCameraView;
-
-pub struct TransparentUI {
-    pub sort_key: FloatOrd,
-    pub entity: Entity,
-    pub pipeline: CachedRenderPipelineId,
-    pub draw_function: DrawFunctionId,
-}
-
-impl PhaseItem for TransparentUI {
-    type SortKey = FloatOrd;
-
-    #[inline]
-    fn sort_key(&self) -> Self::SortKey {
-        self.sort_key
-    }
-
-    #[inline]
-    fn draw_function(&self) -> DrawFunctionId {
-        self.draw_function
-    }
-}
-
-pub struct MainPassUINode {
-    query: QueryState<
-        (
-            &'static RenderPhase<TransparentUI>,
-            &'static ViewTarget,
-            &'static CameraUIKayak,
-        ),
-        With<ExtractedView>,
-    >,
-    default_camera_view_query: QueryState<&'static DefaultCameraView>,
-}
-
-impl MainPassUINode {
-    pub const IN_VIEW: &'static str = "view";
-
-    pub fn new(world: &mut World) -> Self {
-        Self {
-            query: world.query_filtered(),
-            default_camera_view_query: world.query(),
-        }
-    }
-}
-
-impl Node for MainPassUINode {
-    fn input(&self) -> Vec<SlotInfo> {
-        vec![SlotInfo::new(MainPassUINode::IN_VIEW, SlotType::Entity)]
-    }
-
-    fn update(&mut self, world: &mut World) {
-        self.query.update_archetypes(world);
-        self.default_camera_view_query.update_archetypes(world);
-    }
-
-    fn run(
-        &self,
-        graph: &mut RenderGraphContext,
-        render_context: &mut RenderContext,
-        world: &World,
-    ) -> Result<(), NodeRunError> {
-        let input_view_entity = graph.get_input_entity(Self::IN_VIEW)?;
-        // adapted from bevy itself;
-        // see: <https://github.com/bevyengine/bevy/commit/09a3d8abe062984479bf0e99fcc1508bb722baf6>
-        let (transparent_phase, target, _camera_ui) = match self.query.get_manual(world, input_view_entity)
-        {
-            Ok(it) => it,
-            _ => return Ok(()),
-        };
-
-        let view_entity = if let Ok(default_view) = self
-            .default_camera_view_query
-            .get_manual(world, input_view_entity)
-        {
-            default_view.0
-        } else {
-            input_view_entity
-        };
-
-        // let clear_color = world.get_resource::<ClearColor>().unwrap();
-        {
-            let pass_descriptor = RenderPassDescriptor {
-                label: Some("main_transparent_pass_UI"),
-                color_attachments: &[Some(target.get_unsampled_color_attachment(Operations {
-                    // load: match camera_ui.clear_color {
-                    //     ClearColorConfig::Default => {
-                    //         LoadOp::Clear(world.resource::<ClearColor>().0.into())
-                    //     }
-                    //     ClearColorConfig::Custom(color) => LoadOp::Clear(color.into()),
-                    //     ClearColorConfig::None => LoadOp::Load,
-                    // },
-                    load: LoadOp::Load,
-                    store: true,
-                }))],
-                depth_stencil_attachment: None,
-            };
-
-            let draw_functions = world
-                .get_resource::<DrawFunctions<TransparentUI>>()
-                .unwrap();
-
-            let render_pass = render_context
-                .command_encoder
-                .begin_render_pass(&pass_descriptor);
-            let mut draw_functions = draw_functions.write();
-            let mut tracked_pass = TrackedRenderPass::new(render_pass);
-            for item in transparent_phase.items.iter() {
-                let draw_function = draw_functions.get_mut(item.draw_function).unwrap();
-                draw_function.draw(world, &mut tracked_pass, view_entity, item);
-            }
-        }
-
-        Ok(())
-    }
-}
+use bevy::ecs::prelude::*;
+use bevy::render::render_phase::{DrawFunctionId, PhaseItem};
+use bevy::render::render_resource::CachedRenderPipelineId;
+use bevy::render::{
+    render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
+    render_phase::{DrawFunctions, RenderPhase, TrackedRenderPass},
+    render_resource::{LoadOp, Operations, RenderPassDescriptor},
+    renderer::RenderContext,
+    view::{ExtractedView, ViewTarget},
+};
+use bevy::utils::FloatOrd;
+
+use crate::CameraUIKayak;
+
+use super::extract::DefaultCameraView;
+
+pub struct TransparentUI {
+    pub sort_key: FloatOrd,
+    pub entity: Entity,
+    pub pipeline: CachedRenderPipelineId,
+    pub draw_function: DrawFunctionId,
+}
+
+impl PhaseItem for TransparentUI {
+    type SortKey = FloatOrd;
+
+    #[inline]
+    fn sort_key(&self) -> Self::SortKey {
+        self.sort_key
+    }
+
+    #[inline]
+    fn draw_function(&self) -> DrawFunctionId {
+        self.draw_function
+    }
+}
+
+pub struct MainPassUINode {
+    query: QueryState<
+        (
+            &'static RenderPhase<TransparentUI>,
+            &'static ViewTarget,
+            &'static CameraUIKayak,
+        ),
+        With<ExtractedView>,
+    >,
+    default_camera_view_query: QueryState<&'static DefaultCameraView>,
+}
+
+impl MainPassUINode {
+    pub const IN_VIEW: &'static str = "view";
+
+    pub fn new(world: &mut World) -> Self {
+        Self {
+            query: world.query_filtered(),
+            default_camera_view_query: world.query(),
+        }
+    }
+}
+
+impl Node for MainPassUINode {
+    fn input(&self) -> Vec<SlotInfo> {
+        vec![SlotInfo::new(MainPassUINode::IN_VIEW, SlotType::Entity)]
+    }
+
+    fn update(&mut self, world: &mut World) {
+        self.query.update_archetypes(world);
+        self.default_camera_view_query.update_archetypes(world);
+    }
+
+    fn run(
+        &self,
+        graph: &mut RenderGraphContext,
+        render_context: &mut RenderContext,
+        world: &World,
+    ) -> Result<(), NodeRunError> {
+        let input_view_entity = graph.get_input_entity(Self::IN_VIEW)?;
+        // adapted from bevy itself;
+        // see: <https://github.com/bevyengine/bevy/commit/09a3d8abe062984479bf0e99fcc1508bb722baf6>
+        let (transparent_phase, target, _camera_ui) =
+            match self.query.get_manual(world, input_view_entity) {
+                Ok(it) => it,
+                _ => return Ok(()),
+            };
+
+        let view_entity = if let Ok(default_view) = self
+            .default_camera_view_query
+            .get_manual(world, input_view_entity)
+        {
+            default_view.0
+        } else {
+            input_view_entity
+        };
+
+        // let clear_color = world.get_resource::<ClearColor>().unwrap();
+        {
+            let pass_descriptor = RenderPassDescriptor {
+                label: Some("main_transparent_pass_UI"),
+                color_attachments: &[Some(target.get_unsampled_color_attachment(Operations {
+                    // load: match camera_ui.clear_color {
+                    //     ClearColorConfig::Default => {
+                    //         LoadOp::Clear(world.resource::<ClearColor>().0.into())
+                    //     }
+                    //     ClearColorConfig::Custom(color) => LoadOp::Clear(color.into()),
+                    //     ClearColorConfig::None => LoadOp::Load,
+                    // },
+                    load: LoadOp::Load,
+                    store: true,
+                }))],
+                depth_stencil_attachment: None,
+            };
+
+            let draw_functions = world
+                .get_resource::<DrawFunctions<TransparentUI>>()
+                .unwrap();
+
+            let render_pass = render_context
+                .command_encoder
+                .begin_render_pass(&pass_descriptor);
+            let mut draw_functions = draw_functions.write();
+            let mut tracked_pass = TrackedRenderPass::new(render_pass);
+            for item in transparent_phase.items.iter() {
+                let draw_function = draw_functions.get_mut(item.draw_function).unwrap();
+                draw_function.draw(world, &mut tracked_pass, view_entity, item);
+            }
+        }
+
+        Ok(())
+    }
+}
diff --git a/src/render/unified/pipeline.rs b/src/render/unified/pipeline.rs
index 5d2cfc942c68e546fc129ee959b7b553e05c25c9..fcf9c90d5df5218eb2a5c84b13c36871d98946aa 100644
--- a/src/render/unified/pipeline.rs
+++ b/src/render/unified/pipeline.rs
@@ -1,8 +1,8 @@
-use bevy::prelude::{Msaa, Rect, Resource};
+use bevy::prelude::{Rect, Resource};
 use bevy::render::render_resource::{
     DynamicUniformBuffer, ShaderType, SpecializedRenderPipeline, SpecializedRenderPipelines,
 };
-use bevy::render::view::{ViewTarget, ExtractedView};
+use bevy::render::view::{ExtractedView, ViewTarget};
 use bevy::utils::FloatOrd;
 use bevy::{
     ecs::system::{
@@ -526,7 +526,6 @@ pub fn queue_quads(
     mut image_bind_groups: ResMut<ImageBindGroups>,
     unified_pipeline: Res<UnifiedPipeline>,
     gpu_images: Res<RenderAssets<Image>>,
-    msaa: Res<Msaa>,
 ) {
     if let Some(type_binding) = sprite_meta.types_buffer.binding() {
         sprite_meta.types_bind_group =
@@ -553,6 +552,9 @@ pub fn queue_quads(
         let draw_quad = draw_functions.read().get_id::<DrawUI>().unwrap();
         for (camera_entity, mut transparent_phase, view) in views.iter_mut() {
             for (entity, quad) in extracted_sprites.iter_mut() {
+                if camera_entity != quad.camera_entity {
+                    continue;
+                }
                 if let Some(image_handle) = quad.image.as_ref() {
                     if let Some(gpu_image) = gpu_images.get(image_handle) {
                         image_bind_groups
diff --git a/src/render/unified/shader.wgsl b/src/render/unified/shader.wgsl
index cc6061d4ceb11fffbbac17adbff3e94f22de3a0b..ff1841377cfef4f93a3541e571b1c1806cef6b55 100644
--- a/src/render/unified/shader.wgsl
+++ b/src/render/unified/shader.wgsl
@@ -71,7 +71,7 @@ fn sample_sdf(coords: vec2<f32>, arr: i32, scale: f32) -> f32 {
 }
 
 fn range_curve(font_size: f32) -> f32 {
-    return (8.528 - 9.428 * font_size + 3.428 * pow(font_size, 2.0)) + 1.0;
+    return (5.128 - 6.428 * font_size + 3.428 * pow(font_size, 2.0)) + 1.0;
 }
 
 @fragment