diff --git a/Cargo.toml b/Cargo.toml
index 817f1117de33057fb3176d30a608ad546c0fe804..6413a505c0f78256c7d138a02170f32278cad381 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,7 +16,7 @@ members = ["kayak_ui_macros", "kayak_font"]
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-bevy = { version = "0.10", default-features = false, features = ["bevy_render", "bevy_asset", "bevy_winit", "bevy_core_pipeline"] }
+bevy = { version = "0.10", default-features = false, features = ["bevy_render", "bevy_asset", "bevy_winit", "bevy_core_pipeline", "bevy_ui"] }
 bytemuck = "1.12"
 dashmap = "5.4"
 kayak_font = { path = "./kayak_font", version = "0.2" }
diff --git a/examples/bevy_scene.rs b/examples/bevy_scene.rs
index ce16c362322ab52cd230832fd28d251f4409b350..7750894247a12d778a4bd99787adb15e9e6691c7 100644
--- a/examples/bevy_scene.rs
+++ b/examples/bevy_scene.rs
@@ -116,7 +116,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 a3dd07ff395b6f65c4d50fa86f71efc8fd2b5913..20d9c341212cfaac5062d668f750e267d59cd7b0 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 0b8cac2a6c37a47847da5816c348b9e4d4ae47a4..9c4603c16fe7908ba60fd7b823e4674537489946 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 d4dcb75124f27c8142909da7de6e7a9b57e2fba7..4f2d649fc3baa96b580a3b332b0b591368ae1996 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 431876e8ae9704f3e8814237c85a07ab0bc1d56b..771b6a211c0e59a55c5e2d83180a5e40f54bad0a 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 284771afa3e12d28adb673508e50bcad59a01d2c..e6ee73f9e5a4af1955cd62664ed9c68ddfc198c6 100644
--- a/examples/render_target.rs
+++ b/examples/render_target.rs
@@ -57,7 +57,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! {
@@ -82,18 +97,7 @@ fn startup(
             />
         </KayakAppBundle>
     };
-
-    commands.spawn(UICameraBundle {
-        camera: Camera {
-            order: -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
@@ -128,14 +132,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! {
@@ -149,7 +156,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 4b1b64cbf3bb0b107859bcac0acd25ac7b01e094..82e2c28ab7b8c922e73582a5045b804527e0f494 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 4f59b2643d45e9415a25a9f913ce8a1795931ce4..503a4ed738ae95249df095724729591ecabeb240 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 2dc0a11e451bd304c63ddb3c2c86b1ea1c30f9e1..66b12c2b7d34fd2acc04cfdb90424178519f2af1 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/mod.rs b/src/camera/mod.rs
index 9751b184744a1101425cec96208ba46d11e81ffe..f260d649c4ff1ded0f0a3befe5b44e5f7697b1b2 100644
--- a/src/camera/mod.rs
+++ b/src/camera/mod.rs
@@ -1,23 +1,24 @@
-use bevy::{
-    prelude::{CoreSet, IntoSystemConfig, 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(
-            bevy::render::camera::camera_system::<UIOrthographicProjection>
-                .in_base_set(CoreSet::PostUpdate),
-        )
-        .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>;
+    type Out = CameraUIKayak;
+
+    fn extract_component(item: QueryItem<Self::Query>) -> Option<Self::Out> {
+        Some(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/context.rs b/src/context.rs
index 16229526648ee2ab90a753c089d9fc24d42bcf66..3b19a7f35783feee29afd7295e6e9721c6c36403 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -65,7 +65,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() {
@@ -91,17 +91,18 @@ pub struct KayakRootContext {
     pub(crate) order_tree: Arc<RwLock<Tree>>,
     pub(crate) index: Arc<RwLock<HashMap<Entity, usize>>>,
     pub(crate) uninitilized_systems: HashSet<String>,
+    pub camera_entity: Entity,
 }
 
 impl Default for KayakRootContext {
     fn default() -> Self {
-        Self::new()
+        Self::new(Entity::from_raw(0))
     }
 }
 
 impl KayakRootContext {
     /// Creates a new widget context.
-    pub fn new() -> Self {
+    pub fn new(camera_entity: Entity) -> Self {
         Self {
             tree: Arc::new(RwLock::new(Tree::default())),
             layout_cache: Arc::new(RwLock::new(LayoutCache::default())),
@@ -116,6 +117,7 @@ impl KayakRootContext {
             index: Default::default(),
             order_tree: Default::default(),
             uninitilized_systems: Default::default(),
+            camera_entity,
         }
     }
 
@@ -484,7 +486,7 @@ fn update_widgets_sys(world: &mut World) {
         world,
     );
 
-    for (camera_entity, mut context) in context_data.drain(..) {
+    for (entity, mut context) in context_data.drain(..) {
         for system_id in context.uninitilized_systems.drain() {
             if let Some(system) = context.systems.get_mut(&system_id) {
                 system.0.initialize(world);
@@ -517,7 +519,7 @@ fn update_widgets_sys(world: &mut World) {
 
         // dbg!("Updating widgets!");
         update_widgets(
-            camera_entity,
+            context.camera_entity,
             world,
             &context.tree,
             &context.layout_cache,
@@ -565,7 +567,7 @@ fn update_widgets_sys(world: &mut World) {
             indices.clear();
         }
 
-        world.entity_mut(camera_entity).insert(context);
+        world.entity_mut(entity).insert(context);
     }
 }
 
@@ -587,14 +589,14 @@ fn update_widgets(
 ) {
     for entity in widgets.iter() {
         // A small hack to add parents to widgets
-        let mut command_queue = CommandQueue::default();
-        {
-            let mut commands = Commands::new(&mut command_queue, &world);
-            if let Some(mut entity_commands) = commands.get_entity(entity.0) {
-                entity_commands.set_parent(camera_entity);
-            }
-        }
-        command_queue.apply(world);
+        // let mut command_queue = CommandQueue::default();
+        // {
+        //     let mut commands = Commands::new(&mut command_queue, &world);
+        //     if let Some(mut entity_commands) = commands.get_entity(entity.0) {
+        //         entity_commands.set_parent(camera_entity);
+        //     }
+        // }
+        // command_queue.apply(world);
 
         if let Some(entity_ref) = world.get_entity(entity.0) {
             if let Some(widget_type) = entity_ref.get::<WidgetName>() {
@@ -809,6 +811,7 @@ fn update_widget(
         let new_tick = widget_update_system.get_last_change_tick();
         new_ticks.insert(widget_type.clone(), new_tick);
         widget_update_system.set_last_change_tick(old_tick);
+        widget_update_system.apply_buffers(world);
 
         if should_rerender {
             if let Ok(cloned_widget_entities) = cloned_widget_entities.try_read() {
@@ -913,7 +916,7 @@ fn update_widget(
     // Mark node as needing a recalculation of rendering/layout.
     commands.entity(entity.0).insert(DirtyNode);
 
-    for (_index, changed_entity, _parent, changes) in diff.changes.iter() {
+    for (_index, changed_entity, parent, changes) in diff.changes.iter() {
         if changes.iter().any(|change| *change == Change::Deleted) {
             // commands.entity(changed_entity.0).despawn();
             // commands.entity(changed_entity.0).remove::<DirtyNode>();
@@ -922,6 +925,10 @@ fn update_widget(
         if changes.iter().any(|change| *change == Change::Inserted) {
             if let Some(mut entity_commands) = commands.get_entity(changed_entity.0) {
                 entity_commands.insert(Mounted);
+                entity_commands.set_parent(parent.0);
+            }
+            if let Some(mut parent_commands) = commands.get_entity(parent.0) {
+                parent_commands.add_child(changed_entity.0);
             }
         }
     }
diff --git a/src/lib.rs b/src/lib.rs
index d6e61f6eab0c82e6bbe55c357cc04a1579d96faf..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::*;
diff --git a/src/render/extract.rs b/src/render/extract.rs
index 0990189be19f8f6fd8fe02ee357106f5af489d04..ce034cedd4ddddc980666397f51b8d2864c9f376 100644
--- a/src/render/extract.rs
+++ b/src/render/extract.rs
@@ -3,13 +3,19 @@ use crate::{
     node::Node,
     render_primitive::RenderPrimitive,
     styles::Corner,
+    CameraUIKayak,
 };
 use bevy::{
     prelude::{
-        Assets, Camera, Color, Commands, Entity, Image, IntoSystemAppConfig, Plugin, Query, Rect,
-        Res, Vec2, With,
+        Assets, Camera, Camera2d, Camera3d, Color, Commands, Component, Entity, GlobalTransform,
+        Image, IntoSystemAppConfig, IntoSystemAppConfigs, Mat4, Plugin, Query, Rect, Res, UVec4,
+        Vec2, With,
+    },
+    render::{
+        render_phase::RenderPhase,
+        view::{ColorGrading, ExtractedView},
+        Extract, ExtractSchedule, RenderApp,
     },
-    render::{Extract, ExtractSchedule, RenderApp},
     window::{PrimaryWindow, Window, WindowRef},
 };
 use kayak_font::KayakFont;
@@ -17,6 +23,7 @@ use kayak_font::KayakFont;
 use super::{
     font::{self, FontMapping},
     image, nine_patch, texture_atlas,
+    ui_pass::TransparentUI,
     unified::pipeline::{ExtractQuadBundle, ExtractedQuad, UIQuadType},
 };
 
@@ -29,43 +36,52 @@ impl Plugin for BevyKayakUIExtractPlugin {
     fn build(&self, app: &mut bevy::prelude::App) {
         let render_app = app.sub_app_mut(RenderApp);
         render_app.add_system(extract.in_schedule(ExtractSchedule));
+        render_app.add_systems(
+            (
+                extract_default_ui_camera_view::<Camera2d>,
+                extract_default_ui_camera_view::<Camera3d>,
+            )
+                .in_schedule(ExtractSchedule),
+        );
     }
 }
 
 pub fn extract(
     mut commands: Commands,
-    context_query: Extract<Query<(Entity, &KayakRootContext, &Camera)>>,
+    context_query: Extract<Query<(Entity, &KayakRootContext)>>,
     fonts: Extract<Res<Assets<KayakFont>>>,
     font_mapping: Extract<Res<FontMapping>>,
     node_query: Extract<Query<&Node>>,
     widget_names: Extract<Query<&WidgetName>>,
     images: Extract<Res<Assets<Image>>>,
     primary_window: Extract<Query<&Window, With<PrimaryWindow>>>,
-    windows: Extract<Query<&Window>>,
+    cameras: Extract<Query<&Camera>>,
 ) {
     let mut render_primitives = Vec::new();
-    for (entity, context, camera) in context_query.iter() {
-        let dpi = match &camera.target {
-            bevy::render::camera::RenderTarget::Window(window_ref) => match window_ref {
-                WindowRef::Primary => {
-                    if let Ok(window) = primary_window.get_single() {
-                        window.scale_factor() as f32
-                    } else {
-                        1.0
-                    }
-                }
-                WindowRef::Entity(window_entity) => {
-                    if let Ok(window) = windows.get(*window_entity) {
-                        window.scale_factor() as f32
-                    } else {
-                        1.0
+    for (_entity, context) in context_query.iter() {
+        let dpi = if let Ok(camera) = cameras.get(context.camera_entity) {
+            match &camera.target {
+                bevy::render::camera::RenderTarget::Window(window_ref) => match window_ref {
+                    WindowRef::Primary => {
+                        if let Ok(window) = primary_window.get_single() {
+                            window.scale_factor() as f32
+                        } else {
+                            1.0
+                        }
                     }
-                }
-            },
-            _ => 1.0,
+                    _ => 1.0,
+                },
+                _ => 1.0,
+            }
+        } else {
+            1.0
         };
         let mut new_render_primitives = context.build_render_primitives(&node_query, &widget_names);
-        render_primitives.extend(new_render_primitives.drain(..).map(|r| (entity, dpi, r)));
+        render_primitives.extend(
+            new_render_primitives
+                .drain(..)
+                .map(|r| (context.camera_entity, dpi, r)),
+        );
     }
 
     let mut extracted_quads = Vec::new();
@@ -133,3 +149,48 @@ pub fn extract(
     // dbg!(&extracted_quads);
     commands.spawn_batch(extracted_quads);
 }
+
+#[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 {
+        if let (Some(logical_size), Some((physical_origin, _)), Some(physical_size)) = (
+            camera.logical_viewport_size(),
+            camera.physical_viewport_rect(),
+            camera.physical_viewport_size(),
+        ) {
+            // use a projection matrix with the origin in the top left instead of the bottom left that comes with OrthographicProjection
+            let projection_matrix =
+                Mat4::orthographic_rh(0.0, logical_size.x, logical_size.y, 0.0, 0.0, 1000.0);
+            let default_camera_view = commands
+                .spawn(ExtractedView {
+                    projection: projection_matrix,
+                    transform: GlobalTransform::from_xyz(
+                        0.0,
+                        0.0,
+                        1000.0 + UI_CAMERA_TRANSFORM_OFFSET,
+                    ),
+                    hdr: camera.hdr,
+                    viewport: UVec4::new(
+                        physical_origin.x,
+                        physical_origin.y,
+                        physical_size.x,
+                        physical_size.y,
+                    ),
+                    view_projection: None,
+                    color_grading: ColorGrading::default(),
+                })
+                .id();
+            commands.get_or_spawn(entity).insert((
+                DefaultCameraView(default_camera_view),
+                RenderPhase::<TransparentUI>::default(),
+            ));
+        }
+    }
+}
diff --git a/src/render/ui_pass.rs b/src/render/ui_pass.rs
index 89c510f3f0ada0505848f1d858ce75d5e98df690..d7c8edf588d5ff6e44ca721783e44b7ce57f9b60 100644
--- a/src/render/ui_pass.rs
+++ b/src/render/ui_pass.rs
@@ -1,117 +1,123 @@
-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},
-    render_resource::{LoadOp, Operations, RenderPassDescriptor},
-    renderer::RenderContext,
-    view::{ExtractedView, ViewTarget},
-};
-use bevy::utils::FloatOrd;
-
-use crate::CameraUIKayak;
-
-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
-    }
-
-    fn entity(&self) -> Entity {
-        self.entity
-    }
-}
-
-pub struct MainPassUINode {
-    query: QueryState<
-        (
-            &'static RenderPhase<TransparentUI>,
-            &'static ViewTarget,
-            &'static CameraUIKayak,
-        ),
-        With<ExtractedView>,
-    >,
-}
-
-impl MainPassUINode {
-    pub const IN_VIEW: &'static str = "view";
-
-    pub fn new(world: &mut World) -> Self {
-        Self {
-            query: world.query_filtered(),
-        }
-    }
-}
-
-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);
-    }
-
-    fn run(
-        &self,
-        graph: &mut RenderGraphContext,
-        render_context: &mut RenderContext,
-        world: &World,
-    ) -> Result<(), NodeRunError> {
-        let 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, view_entity)
-        {
-            Ok(it) => it,
-            _ => return Ok(()),
-        };
-        // 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,
-                    },
-                    store: true,
-                }))],
-                depth_stencil_attachment: None,
-            };
-
-            let draw_functions = world
-                .get_resource::<DrawFunctions<TransparentUI>>()
-                .unwrap();
-
-            let mut tracked_pass = render_context.begin_tracked_render_pass(pass_descriptor);
-            let mut draw_functions = draw_functions.write();
-            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},
+    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
+    }
+
+    fn entity(&self) -> Entity {
+        self.entity
+    }
+}
+
+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: LoadOp::Load,
+                    store: true,
+                }))],
+                depth_stencil_attachment: None,
+            };
+
+            let draw_functions = world
+                .get_resource::<DrawFunctions<TransparentUI>>()
+                .unwrap();
+
+            let mut tracked_pass = render_context.begin_tracked_render_pass(pass_descriptor);
+            let mut draw_functions = draw_functions.write();
+            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 adc75a1b383733e65c853aa6e4e9147ea145efff..96bf6f80e5047c780d1a8e8937c5c3c61f0d12a7 100644
--- a/src/render/unified/pipeline.rs
+++ b/src/render/unified/pipeline.rs
@@ -1,7 +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::{ExtractedView, ViewTarget};
 use bevy::utils::FloatOrd;
 use bevy::{
     ecs::system::{
@@ -67,27 +68,10 @@ impl FontRenderingPipeline for UnifiedPipeline {
     }
 }
 
-bitflags::bitflags! {
-    #[repr(transparent)]
-    pub struct UnifiedPipelineKey: u32 {
-        const NONE                        = 0;
-        const MSAA_RESERVED_BITS          = Self::MSAA_MASK_BITS << Self::MSAA_SHIFT_BITS;
-    }
-}
-
-impl UnifiedPipelineKey {
-    const MSAA_MASK_BITS: u32 = 0b111;
-    const MSAA_SHIFT_BITS: u32 = 32 - Self::MSAA_MASK_BITS.count_ones();
-
-    pub fn from_msaa_samples(msaa_samples: u32) -> Self {
-        let msaa_bits =
-            (msaa_samples.trailing_zeros() & Self::MSAA_MASK_BITS) << Self::MSAA_SHIFT_BITS;
-        Self::from_bits(msaa_bits).unwrap()
-    }
-
-    pub fn msaa_samples(&self) -> u32 {
-        1 << ((self.bits >> Self::MSAA_SHIFT_BITS) & Self::MSAA_MASK_BITS)
-    }
+#[derive(Debug, Component, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct UnifiedPipelineKey {
+    pub msaa: u32,
+    pub hdr: bool,
 }
 
 impl FromWorld for UnifiedPipeline {
@@ -244,7 +228,7 @@ impl FromWorld for UnifiedPipeline {
 impl SpecializedRenderPipeline for UnifiedPipeline {
     type Key = UnifiedPipelineKey;
 
-    fn specialize(&self, _key: Self::Key) -> RenderPipelineDescriptor {
+    fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
         let vertex_buffer_layout = VertexBufferLayout {
             array_stride: 60,
             step_mode: VertexStepMode::Vertex,
@@ -284,7 +268,11 @@ impl SpecializedRenderPipeline for UnifiedPipeline {
                 shader_defs: vec![],
                 entry_point: "fragment".into(),
                 targets: vec![Some(ColorTargetState {
-                    format: TextureFormat::bevy_default(),
+                    format: if key.hdr {
+                        ViewTarget::TEXTURE_FORMAT_HDR
+                    } else {
+                        TextureFormat::bevy_default()
+                    },
                     blend: Some(BlendState {
                         color: BlendComponent {
                             src_factor: BlendFactor::SrcAlpha,
@@ -317,7 +305,7 @@ impl SpecializedRenderPipeline for UnifiedPipeline {
             },
             depth_stencil: None,
             multisample: MultisampleState {
-                count: 1,
+                count: key.msaa,
                 mask: !0,
                 alpha_to_coverage_enabled: false,
             },
@@ -359,7 +347,7 @@ pub struct ExtractedQuad {
 }
 
 #[repr(C)]
-#[derive(Copy, Clone, Pod, Zeroable)]
+#[derive(Copy, Clone)]
 struct QuadVertex {
     pub position: [f32; 3],
     pub color: [f32; 4],
@@ -367,6 +355,9 @@ struct QuadVertex {
     pub pos_size: [f32; 4],
 }
 
+unsafe impl Zeroable for QuadVertex {}
+unsafe impl Pod for QuadVertex {}
+
 #[repr(C)]
 #[derive(Copy, Clone, ShaderType)]
 struct QuadType {
@@ -537,11 +528,10 @@ pub fn queue_quads(
     mut pipelines: ResMut<SpecializedRenderPipelines<UnifiedPipeline>>,
     mut pipeline_cache: ResMut<PipelineCache>,
     mut extracted_sprites: Query<(Entity, &ExtractedQuad)>,
-    mut views: Query<(Entity, &mut RenderPhase<TransparentUI>)>,
+    mut views: Query<(Entity, &mut RenderPhase<TransparentUI>, &ExtractedView)>,
     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 =
@@ -565,13 +555,16 @@ pub fn queue_quads(
             layout: &quad_pipeline.view_layout,
         }));
 
-        let key = UnifiedPipelineKey::from_msaa_samples(msaa.samples());
-        let spec_pipeline = pipelines.specialize(&mut pipeline_cache, &quad_pipeline, key);
-
         let draw_quad = draw_functions.read().get_id::<DrawUI>().unwrap();
-        for (camera_entity, mut transparent_phase) in views.iter_mut() {
+        for (camera_entity, mut transparent_phase, view) in views.iter_mut() {
+            let key = UnifiedPipelineKey {
+                msaa: 1,
+                hdr: view.hdr,
+            };
+            let spec_pipeline = pipelines.specialize(&mut pipeline_cache, &quad_pipeline, key);
+
             for (entity, quad) in extracted_sprites.iter_mut() {
-                if quad.camera_entity != camera_entity {
+                if camera_entity != quad.camera_entity {
                     continue;
                 }
                 if let Some(image_handle) = quad.image.as_ref() {
@@ -599,6 +592,7 @@ pub fn queue_quads(
                             });
                     }
                 }
+
                 transparent_phase.add(TransparentUI {
                     draw_function: draw_quad,
                     pipeline: spec_pipeline,
diff --git a/src/render/unified/shader.wgsl b/src/render/unified/shader.wgsl
index 4341b7e53853eb9b70eaa674b8d79523ba004cfa..122cfb872ee9a1025487f9500e67e9032db2f1bf 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
diff --git a/src/widget_context.rs b/src/widget_context.rs
index c6e64c770389ec4737fb9c45fd6fd9ada62f9f69..5f5b95ed41c68cc876f0abd06ff53e345c68d4b0 100644
--- a/src/widget_context.rs
+++ b/src/widget_context.rs
@@ -225,6 +225,21 @@ impl KayakWidgetContext {
         }
     }
 
+    // Despawns a widget entity and it's decedents. This is done in a safe way by keeping entity id's around.
+    pub fn despawn_safe(&self, commands: &mut Commands, entity: Entity) {
+        if let Ok(mut tree) = self.old_tree.write() {
+            let mut down_iter = tree.down_iter();
+            down_iter.current_node = Some(WrappedIndex(entity));
+            for child in down_iter {
+                commands.entity(child.0).despawn();
+                commands.get_or_spawn(child.0);
+            }
+            commands.entity(entity).despawn();
+            commands.get_or_spawn(entity);
+            tree.remove(WrappedIndex(entity));
+        }
+    }
+
     /// Attempts to get the layout rect for the widget with the given ID
     ///
     /// # Arguments