kitgit

tirbofish/dropbear · commit

2437ae2233ecb565a0d94ad6b0e76d41ec500bb1

wip: got yakui ui to work, and i should be able to create a little "buffer" typa thing. hooray! Unverified

Thribhu K <4tkbytes@pm.me> · 2026-01-23 13:03

view full diff

diff --git a/.cargo/config.toml b/.cargo/config.toml
index d2312c6..d29d6c3 100644
--- a/.cargo/config.toml
+++ b/.cargo/config.toml
@@ -1,3 +1,3 @@
 [target.x86_64-unknown-linux-gnu]
 linker = "clang"
-rustflags = ["-C", "link-arg=-fuse-ld=mold", "-C", "relocation-model=pic"]
+rustflags = ["-C", "link-arg=-fuse-ld=mold"]
diff --git a/Cargo.toml b/Cargo.toml
index 8e2dd93..9042336 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -73,6 +73,8 @@ rapier3d = { version = "0.32", features = [ "simd-stable", "serde-serialize" ] }
 cbindgen = { version = "0.29.2" }
 postcard = { version = "1.1"}
 pollster = "0.4"
+yakui = { git = "https://github.com/SecondHalfGames/yakui", rev = "ddf7614" }
+yakui-wgpu = { git = "https://github.com/SecondHalfGames/yakui", rev = "ddf7614" }
 
 [workspace.dependencies.image]
 version = "0.25"
diff --git a/README.md b/README.md
index 212f748..2bd5fb9 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,6 @@ If you might have not realised, all the crates/projects names are after Australi
 - [eucalyptus-core](https://github.com/tirbofish/dropbear/tree/main/crates/eucalyptus-core) is the library used by both `redback-runtime` and `eucalyptus-editor` to share configs and metadata between each other.
 - [redback-runtime](https://github.com/tirbofish/dropbear/tree/main/crates/redback-runtime) is the runtime used to load .eupak files and run the game loaded on them.
 - [magna-carta](https://github.com/tirbofish/dropbear/tree/main/crates/magna-carta) is a rust library used to generate compile-time Kotlin/Native and Kotlin/JVM metadata for searching.
-- [kino-gui](https://github.com/tirbofish/dropbear/tree/main/crates/kino-gui) is a wgpu primary UI renderer used for displaying shapes, text, and images in a simple immediate-mode way. 
 
 [//]: # (- [eucalyptus-sdk]&#40;https://github.com/tirbofish/dropbear/tree/main/eucalyptus-sdk&#41; is used to develop plugins to be used with the `eucalyptus-editor`)
 
diff --git a/crates/dropbear-engine/Cargo.toml b/crates/dropbear-engine/Cargo.toml
index b80cba2..8897280 100644
--- a/crates/dropbear-engine/Cargo.toml
+++ b/crates/dropbear-engine/Cargo.toml
@@ -43,6 +43,8 @@ dashmap.workspace = true
 typetag.workspace = true
 postcard.workspace = true
 pollster.workspace = true
+yakui-wgpu.workspace = true
+yakui.workspace = true
 
 [target.'cfg(not(target_os = "android"))'.dependencies]
 rfd.workspace = true
diff --git a/crates/dropbear-engine/src/graphics.rs b/crates/dropbear-engine/src/graphics.rs
index 6a085db..bcf8a9f 100644
--- a/crates/dropbear-engine/src/graphics.rs
+++ b/crates/dropbear-engine/src/graphics.rs
@@ -31,6 +31,8 @@ pub struct SharedGraphicsContext {
     pub future_queue: Arc<FutureQueue>,
     pub supports_storage: bool,
     pub mipmapper: Arc<MipMapper>,
+    pub yakui_renderer: Arc<Mutex<yakui_wgpu::YakuiWgpu>>,
+    pub yakui_texture: yakui::TextureId,
 }
 
 impl SharedGraphicsContext {
@@ -73,6 +75,8 @@ impl SharedGraphicsContext {
             surface_format: state.surface_format,
             supports_storage: state.supports_storage,
             mipmapper: state.mipmapper.clone(),
+            yakui_renderer: state.yakui_renderer.clone(),
+            yakui_texture: state.yakui_texture.clone(),
         }
     }
 }
diff --git a/crates/dropbear-engine/src/lib.rs b/crates/dropbear-engine/src/lib.rs
index e1aa092..e327b00 100644
--- a/crates/dropbear-engine/src/lib.rs
+++ b/crates/dropbear-engine/src/lib.rs
@@ -98,6 +98,8 @@ pub struct State {
     physics_accumulator: Duration,
 
     pub scene_manager: scene::Manager,
+    pub yakui_renderer: Arc<Mutex<yakui_wgpu::YakuiWgpu>>,
+    pub yakui_texture: yakui::TextureId,
 }
 
 impl State {
@@ -330,6 +332,19 @@ Hardware:
         let device = Arc::new(device);
         let queue = Arc::new(queue);
 
+        let yakui_renderer = Arc::new(Mutex::new(yakui_wgpu::YakuiWgpu::new(
+            &device,
+            &queue
+        )));
+
+        let yakui_texture = yakui_renderer.lock().add_texture(
+            viewport_texture.view.clone(),
+            wgpu::FilterMode::default(),
+            wgpu::FilterMode::default(),
+            wgpu::FilterMode::default(),
+            wgpu::AddressMode::default(),
+        );
+
         let result = Self {
             surface: Arc::new(surface),
             surface_format: Texture::TEXTURE_FORMAT,
@@ -357,6 +372,8 @@ Hardware:
                 light_cube_bind_group_layout,
             }),
             supports_storage: supports_storage_resources,
+            yakui_renderer,
+            yakui_texture,
         };
 
         Ok(result)
diff --git a/crates/dropbear-engine/src/procedural/mod.rs b/crates/dropbear-engine/src/procedural/mod.rs
index 695c172..f588ef1 100644
--- a/crates/dropbear-engine/src/procedural/mod.rs
+++ b/crates/dropbear-engine/src/procedural/mod.rs
@@ -10,11 +10,25 @@ use parking_lot::Mutex;
 use std::collections::HashMap;
 use std::hash::{DefaultHasher, Hasher};
 use std::sync::{Arc, LazyLock};
+use serde::{Deserialize, Serialize};
 use wgpu::util::DeviceExt;
 
 pub mod cube;
 // pub mod plane;
 
+#[derive(
+    Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize
+)]
+pub enum ProcObj {
+    /// A parameterized cuboid (box) generated at runtime.
+    ///
+    /// Stored as IEEE-754 `f32` bit patterns so the reference remains hashable.
+    /// Values can be reconstructed with `f32::from_bits`.
+    ///
+    /// The `size_bits` represent the full extents (width, height, depth).
+    Cuboid { size_bits: [u32; 3] },
+}
+
 /// An object that comes with a template, and is generated through parameter input. 
 pub struct ProcedurallyGeneratedObject {
     pub vertices: Vec<ModelVertex>,
diff --git a/crates/dropbear-engine/src/utils.rs b/crates/dropbear-engine/src/utils.rs
index 0c94d07..fe07164 100644
--- a/crates/dropbear-engine/src/utils.rs
+++ b/crates/dropbear-engine/src/utils.rs
@@ -3,6 +3,7 @@
 use serde::{Deserialize, Serialize};
 use std::fmt::{Display, Formatter};
 use std::path::Path;
+use crate::procedural::ProcObj;
 
 pub const EUCA_SCHEME: &str = "euca://";
 
@@ -105,14 +106,10 @@ pub enum ResourceReferenceType {
     /// The content in bytes. Sometimes, there is a model that is loaded into memory through the
     /// [`include_bytes!`] macro, this type stores it.
     Bytes(Vec<u8>),
-
-    /// A parameterized cuboid (box) generated at runtime.
-    ///
-    /// Stored as IEEE-754 `f32` bit patterns so the reference remains hashable.
-    /// Values can be reconstructed with `f32::from_bits`.
-    ///
-    /// The `size_bits` represent the full extents (width, height, depth).
-    Cuboid { size_bits: [u32; 3] },
+    
+    /// An object that can be generated at runtime with the usage of vertices and indices, as well
+    /// as a solid grey mesh. 
+    ProcObj(ProcObj),
 }
 
 impl Default for ResourceReferenceType {
diff --git a/crates/eucalyptus-core/Cargo.toml b/crates/eucalyptus-core/Cargo.toml
index 86be779..d5da3b9 100644
--- a/crates/eucalyptus-core/Cargo.toml
+++ b/crates/eucalyptus-core/Cargo.toml
@@ -13,7 +13,6 @@ crate-type = ["rlib", "dylib"]
 dropbear-traits = { path = "../dropbear-traits" }
 dropbear-macro = { path = "../dropbear-macro" }
 magna-carta = { path = "../magna-carta" }
-kino-gui = { path = "../kino-gui" }
 
 anyhow.workspace = true
 postcard.workspace = true
@@ -44,6 +43,7 @@ semver.workspace = true
 rustc_version_runtime.workspace = true
 rapier3d.workspace = true
 bytemuck.workspace = true
+yakui.workspace = true
 
 [features]
 default = []
diff --git a/crates/eucalyptus-core/src/scene.rs b/crates/eucalyptus-core/src/scene.rs
index 0edd530..c52f982 100644
--- a/crates/eucalyptus-core/src/scene.rs
+++ b/crates/eucalyptus-core/src/scene.rs
@@ -28,6 +28,7 @@ use std::path::{Path, PathBuf};
 use std::sync::Arc;
 use crossbeam_channel::Sender;
 use hecs::Entity;
+use dropbear_engine::procedural::ProcObj;
 use crate::physics::collider::ColliderGroup;
 use crate::physics::kcc::KCC;
 use crate::physics::PhysicsState;
@@ -185,28 +186,32 @@ impl SceneConfig {
                             .await?;
                     MeshRenderer::from_handle_with_import_scale(loaded, import_scale)
                 }
-                ResourceReferenceType::Cuboid { size_bits } => {
-                    let size = [
-                        f32::from_bits(size_bits[0]),
-                        f32::from_bits(size_bits[1]),
-                        f32::from_bits(size_bits[2]),
-                    ];
-                    log::info!("Loading entity from cuboid: {:?}", size);
-                    {
-                        let mut cache_guard = MODEL_CACHE.lock();
-                        cache_guard.remove(label);
-                    }
+                ResourceReferenceType::ProcObj(obj) => {
+                    match obj {
+                        ProcObj::Cuboid { size_bits } => {
+                            let size = [
+                                f32::from_bits(size_bits[0]),
+                                f32::from_bits(size_bits[1]),
+                                f32::from_bits(size_bits[2]),
+                            ];
+                            log::info!("Loading entity from cuboid: {:?}", size);
+                            {
+                                let mut cache_guard = MODEL_CACHE.lock();
+                                cache_guard.remove(label);
+                            }
 
-                    let size_vec = glam::DVec3::new(size[0] as f64, size[1] as f64, size[2] as f64);
-                    let mut loaded_model = dropbear_engine::procedural::ProcedurallyGeneratedObject::cuboid(size_vec)
-                        .build_model(graphics.clone(), None, Some(label));
+                            let size_vec = glam::DVec3::new(size[0] as f64, size[1] as f64, size[2] as f64);
+                            let mut loaded_model = dropbear_engine::procedural::ProcedurallyGeneratedObject::cuboid(size_vec)
+                                .build_model(graphics.clone(), None, Some(label));
 
-                    let model = loaded_model.make_mut();
-                    model.path = ResourceReference::from_reference(ResourceReferenceType::Cuboid { size_bits: *size_bits });
+                            let model = loaded_model.make_mut();
+                            model.path = ResourceReference::from_reference(ResourceReferenceType::ProcObj(ProcObj::Cuboid { size_bits: *size_bits }));
 
-                    loaded_model.refresh_registry();
+                            loaded_model.refresh_registry();
 
-                    MeshRenderer::from_handle_with_import_scale(loaded_model, import_scale)
+                            MeshRenderer::from_handle_with_import_scale(loaded_model, import_scale)
+                        }
+                    }
                 }
             };
 
diff --git a/crates/eucalyptus-core/src/scripting.rs b/crates/eucalyptus-core/src/scripting.rs
index e8a4d7e..760b394 100644
--- a/crates/eucalyptus-core/src/scripting.rs
+++ b/crates/eucalyptus-core/src/scripting.rs
@@ -29,7 +29,7 @@ use dropbear_engine::model::MODEL_CACHE;
 use magna_carta::Target;
 use crate::scene::loading::SCENE_LOADER;
 use crate::types::{CollisionEvent, ContactForceEvent};
-use crate::ui::UI_COMMAND_BUFFER;
+use crate::ui::UI_CONTEXT;
 
 /// The target of the script. This can be either a JVM or a native library.
 #[derive(Default, Clone, Debug)]
@@ -227,7 +227,9 @@ impl ScriptManager {
         let model_cache_ptr = &raw const *MODEL_CACHE;
         ASSET_REGISTRY.add_pointer(Const("model_cache"), model_cache_ptr as usize);
         
-        let ui_buf = &raw const *UI_COMMAND_BUFFER;
+        let ui_buf = UI_CONTEXT.with(|v| {
+            v.as_ptr()
+        });
 
         let context = DropbearContext {
             world,
diff --git a/crates/eucalyptus-core/src/ui.rs b/crates/eucalyptus-core/src/ui.rs
index cf6d021..fdf1724 100644
--- a/crates/eucalyptus-core/src/ui.rs
+++ b/crates/eucalyptus-core/src/ui.rs
@@ -1,191 +1,61 @@
-pub mod rect;
-
-use once_cell::sync::Lazy;
+use std::cell::RefCell;
 use parking_lot::Mutex;
-use kino_gui::prelude::*;
-use std::collections::HashMap;
-
-pub static UI_COMMAND_BUFFER: Lazy<UiContext> = Lazy::new(|| UiContext::new());
-
-#[derive(Clone, Copy, Debug)]
-pub struct UiResponse {
-    pub id: u64,
-    pub was_clicked: bool,
-}
-
-impl UiResponse {
-    pub fn clicked(&self) -> bool {
-        self.was_clicked
-    }
+use serde::{Deserialize, Serialize};
+use yakui::Yakui;
+use dropbear_engine::utils::ResourceReference;
+use dropbear_macro::SerializableComponent;
+use dropbear_traits::SerializableComponent;
+
+thread_local! {
+    pub static UI_CONTEXT: RefCell<UiContext> = RefCell::new(UiContext::new());
 }
 
-pub enum UiCommand {
-    Rect {
-        id: u64,
-        initial: Vector2,
-        size: Size,
-        corner_radius: f32,
-        stroke: f32,
-        fill: Colour,
-        stroke_kind: String,
-    },
-    Circle {
-        id: u64,
-        center: Vector2,
-        radius: f32,
-        fill: Colour,
-        stroke: f32,
-    },
+/// A component that can be attached to an entity that renders UI for the entire scene.
+///
+/// This UI is used in tandem with a `.kts` (Kotlin Script file) with the dropbear-engine scripting
+/// ui DSL.
+#[derive(Debug, Serialize, Deserialize, Clone, SerializableComponent, Default)]
+pub struct UIComponent {
+    /// Does not render the UI file.
+    pub disabled: bool,
+    /// The reference to the script file.
+    pub ui_file: ResourceReference,
 }
 
 pub struct UiContext {
-    commands: Mutex<Vec<UiCommand>>,
-    pub currently_rendering: Mutex<HashMap<u64, UiResponse>>,
+    pub yakui_state: Mutex<Yakui>,
+    pub to_render: Mutex<Vec<Box<dyn FnOnce()>>>,
 }
 
 impl UiContext {
     pub fn new() -> Self {
         Self {
-            commands: Mutex::new(Vec::new()),
-            currently_rendering: Mutex::new(HashMap::new()),
+            yakui_state: Mutex::new(Yakui::new()),
+            to_render: Default::default(),
         }
     }
-
-    pub fn push(&self, command: UiCommand) {
-        self.commands.lock().push(command);
-    }
-
-    pub fn drain_commands(&self) -> Vec<UiCommand> {
-        self.commands.lock().drain(..).collect()
-    }
-
-    pub fn update_responses(&self, responses: HashMap<u64, UiResponse>) {
-        let mut current = self.currently_rendering.lock();
-        *current = responses;
-    }
 }
 
 pub mod jni {
-    #![allow(non_snake_case)]
-
-    use jni::JNIEnv;
-    use jni::sys::{jboolean, jlong};
-    use jni::objects::JObject;
-    use kino_gui::prelude::shapes::Rectangle;
-    use kino_gui::Widget;
-    use crate::{convert_ptr};
-    use crate::scripting::jni::utils::FromJObject;
-    use crate::ui::{UiCommand, UiContext};
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_ui_UINative_pushRect(
-        mut env: JNIEnv,
-        _class: jni::objects::JClass,
-        ui_buffer_handle: jlong,
-        rect: JObject,
-    ) {
-        let ui = convert_ptr!(ui_buffer_handle => UiContext);
+    use jni::sys::jlong;
+    use crate::convert_ptr;
+    use crate::ui::{UiContext};
 
-        let rect = match Rectangle::from_jobject(&mut env, &rect) {
-            Ok(v) => v,
-            Err(e) => {
-                let _ = env.throw_new("java/lang/RuntimeException", format!("Unable to convert scripting::Rectangle->kino::Rectangle: {:?}", e));
-                return;
-            }
-        };
-
-        ui.push(UiCommand::Rect {
-            id: rect.id().as_u64(),
-            initial: rect.initial,
-            size: rect.size,
-            corner_radius: 0.0, // rect.corner_radius when available
-            stroke: 0.0, // rect.stroke when available
-            fill: rect.fill_colour,
-            stroke_kind: "Middle".to_string(), // rect.stroke_kind when available
-        });
-    }
-    
     #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_ui_UINative_pushCircle(
-        mut env: JNIEnv,
+    pub extern "C" fn Java_YourClass_addOverlay(
+        _env: jni::JNIEnv,
         _class: jni::objects::JClass,
-        ui_buffer_handle: jlong,
-        circle: JObject,
+        ui_buf_ptr: jlong,
     ) {
-        let _ui = convert_ptr!(ui_buffer_handle => UiContext);
-
-        // Extract Circle fields
-        let id_obj = match env
-            .get_field(&circle, "id", "Lcom/dropbear/utils/ID;")
-            .and_then(|v| v.l())
-        {
-            Ok(obj) => obj,
-            Err(e) => {
-                let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get id field: {}", e));
-                return;
-            }
-        };
-
-        let _id = match env.call_method(&id_obj, "getId", "()J", &[]).and_then(|v| v.j()) {
-            Ok(val) => val as u64,
-            Err(e) => {
-                let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get id value: {}", e));
-                return;
-            }
-        };
-
-        let center_obj = match env
-            .get_field(&circle, "center", "Lcom/dropbear/math/Vector2d;")
-            .and_then(|v| v.l())
-        {
-            Ok(obj) => obj,
-            Err(e) => {
-                let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get center field: {}", e));
-                return;
-            }
-        };
-
-        let _center_x = match env.get_field(&center_obj, "x", "D").and_then(|v| v.d()) {
-            Ok(val) => val,
-            Err(e) => {
-                let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get center.x: {}", e));
-                return;
-            }
-        };
+        let ui = convert_ptr!(ui_buf_ptr => UiContext);
 
-        let _center_y = match env.get_field(&center_obj, "y", "D").and_then(|v| v.d()) {
-            Ok(val) => val,
-            Err(e) => {
-                let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get center.y: {}", e));
-                return;
-            }
-        };
+        let mut state = ui.to_render.lock();
 
-        let _radius = match env.get_field(&circle, "radius", "D").and_then(|v| v.d()) {
-            Ok(val) => val,
-            Err(e) => {
-                let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get radius: {}", e));
-                return;
-            }
-        };
-
-        panic!("this is not implemented yet :(")
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_ui_UINative_wasClicked(
-        _env: JNIEnv,
-        _class: jni::objects::JClass,
-        ui_buffer_handle: jlong,
-        id: jlong,
-    ) -> jboolean {
-        let ui = convert_ptr!(ui_buffer_handle => UiContext);
-
-        let mut rendering = ui.currently_rendering.lock();
-        if let Some(response) = rendering.get(&(id as u64)) {
-            response.clicked().into()
-        } else {
-            false.into()
-        }
+        state.push(Box::new(move || {
+            // yakui::colored_box(
+                // Color::rgba(255, 0, 0, 128),
+                // yakui::geometry::Vec2::new(width, height)
+            // );
+        }));
     }
 }
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/ui/rect.rs b/crates/eucalyptus-core/src/ui/rect.rs
deleted file mode 100644
index d9eaeea..0000000
--- a/crates/eucalyptus-core/src/ui/rect.rs
+++ /dev/null
@@ -1,198 +0,0 @@
-use jni::JNIEnv;
-use jni::objects::JObject;
-use crate::scripting::jni::utils::{FromJObject, ToJObject};
-use crate::scripting::result::DropbearNativeResult;
-use crate::scripting::native::DropbearNativeError;
-use kino_gui::prelude::*;
-use kino_gui::prelude::shapes::Rectangle;
-use kino_gui::WidgetId;
-
-impl FromJObject for Rectangle {
-    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self>
-    where
-        Self: Sized
-    {
-        let class = env
-            .find_class("com/dropbear/ui/primitive/Rectangle")
-            .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
-
-        if !env
-            .is_instance_of(obj, &class)
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?
-        {
-            return Err(DropbearNativeError::InvalidArgument);
-        }
-
-        // Get the ID field
-        let id_obj = env
-            .get_field(obj, "id", "Lcom/dropbear/utils/ID;")
-            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
-            .l()
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
-        
-        let id = env
-            .call_method(&id_obj, "getId", "()J", &[])
-            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
-            .j()
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as u64;
-
-        // Get initial (Vector2d)
-        let initial_obj = env
-            .get_field(obj, "initial", "Lcom/dropbear/math/Vector2d;")
-            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
-            .l()
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
-
-        let initial_x = env
-            .get_field(&initial_obj, "x", "D")
-            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
-            .d()
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as f32;
-
-        let initial_y = env
-            .get_field(&initial_obj, "y", "D")
-            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
-            .d()
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as f32;
-
-        // Get width and height
-        let width = env
-            .get_field(obj, "width", "D")
-            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
-            .d()
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as f32;
-
-        let height = env
-            .get_field(obj, "height", "D")
-            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
-            .d()
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as f32;
-
-        // Get corner radius
-        let corner_radius = env
-            .get_field(obj, "cornerRadius", "D")
-            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
-            .d()
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as f32;
-
-        // Get stroke width
-        let stroke_width = env
-            .get_field(obj, "stroke", "D")
-            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
-            .d()
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as f32;
-
-        // Get fill colour (Colour object)
-        let fill_colour_obj = env
-            .get_field(obj, "fillColour", "Lcom/dropbear/utils/Colour;")
-            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
-            .l()
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
-
-        let r = env
-            .get_field(&fill_colour_obj, "r", "B")
-            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
-            .b()
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as u8;
-
-        let g = env
-            .get_field(&fill_colour_obj, "g", "B")
-            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
-            .b()
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as u8;
-
-        let b = env
-            .get_field(&fill_colour_obj, "b", "B")
-            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
-            .b()
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as u8;
-
-        let a = env
-            .get_field(&fill_colour_obj, "a", "B")
-            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
-            .b()
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as u8;
-
-        // let fill = egui::Color32::from_rgba_unmultiplied(r, g, b, a);
-        let fill = Colour {
-            r: r as f32 / 255.0,
-            g: g as f32 / 255.0,
-            b: b as f32 / 255.0,
-            a: a as f32 / 255.0,
-        };
-        
-        // Get stroke colour
-        let stroke_colour_obj = env
-            .get_field(obj, "strokeColour", "Lcom/dropbear/utils/Colour;")
-            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
-            .l()
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
-
-        let stroke_r = env
-            .get_field(&stroke_colour_obj, "r", "B")
-            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
-            .b()
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as u8;
-
-        let stroke_g = env
-            .get_field(&stroke_colour_obj, "g", "B")
-            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
-            .b()
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as u8;
-
-        let stroke_b = env
-            .get_field(&stroke_colour_obj, "b", "B")
-            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
-            .b()
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as u8;
-
-        let stroke_a = env
-            .get_field(&stroke_colour_obj, "a", "B")
-            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
-            .b()
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)? as u8;
-
-        let stroke_color = egui::Color32::from_rgba_unmultiplied(stroke_r, stroke_g, stroke_b, stroke_a);
-        // let stroke = Stroke::new(stroke_width, stroke_color);
-
-        // Get stroke kind (enum)
-        let stroke_kind_obj = env
-            .get_field(obj, "strokeKind", "Lcom/dropbear/ui/StrokeKind;")
-            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
-            .l()
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
-
-        let stroke_kind_name = env
-            .call_method(&stroke_kind_obj, "name", "()Ljava/lang/String;", &[])
-            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
-            .l()
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
-
-        let stroke_kind_str: String = env
-            .get_string(&stroke_kind_name.into())
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?
-            .into();
-
-        // let stroke_kind = match stroke_kind_str.as_str() {
-        //     "Inside" => StrokeKind::Inside,
-        //     "Middle" => StrokeKind::Middle,
-        //     "Outside" => StrokeKind::Outside,
-        //     _ => StrokeKind::Middle, // default
-        // };
-
-        let rect = Rectangle::new(
-            WidgetId::new(id),
-            [initial_x, initial_y].into(),
-            [width, height].into(),
-            fill
-        );
-        
-        Ok(rect)
-    }
-}
-
-impl ToJObject for Rectangle {
-    fn to_jobject<'a>(&self, _env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
-        todo!()
-    }
-}
\ No newline at end of file
diff --git a/crates/eucalyptus-editor/Cargo.toml b/crates/eucalyptus-editor/Cargo.toml
index b43f599..7d8ea76 100644
--- a/crates/eucalyptus-editor/Cargo.toml
+++ b/crates/eucalyptus-editor/Cargo.toml
@@ -54,6 +54,7 @@ rfd.workspace = true
 semver.workspace = true
 serde.workspace = true
 
+
 [target.'cfg(unix)'.dependencies]
 daemonize = "0.5.0"
 
diff --git a/crates/eucalyptus-editor/src/editor/component.rs b/crates/eucalyptus-editor/src/editor/component.rs
index cdc18c6..fd152e7 100644
--- a/crates/eucalyptus-editor/src/editor/component.rs
+++ b/crates/eucalyptus-editor/src/editor/component.rs
@@ -6,7 +6,7 @@ use dropbear_engine::attenuation::ATTENUATION_PRESETS;
 use dropbear_engine::entity::{EntityTransform, MeshRenderer, Transform};
 use dropbear_engine::lighting::LightType;
 use dropbear_engine::texture::TextureWrapMode;
-use dropbear_engine::utils::ResourceReferenceType;
+use dropbear_engine::utils::{ResourceReference, ResourceReferenceType};
 use egui::{CollapsingHeader, ComboBox, DragValue, Grid, RichText, TextEdit, Ui, UiBuilder};
 use eucalyptus_core::camera::CameraType;
 use eucalyptus_core::states::{Camera3D, Light, Property, Script};
@@ -14,7 +14,9 @@ use eucalyptus_core::{fatal, warn};
 use glam::{DVec3, Vec3};
 use hecs::Entity;
 use std::time::Instant;
+use dropbear_engine::procedural::ProcObj;
 use eucalyptus_core::properties::{CustomProperties, Value};
+use eucalyptus_core::ui::UIComponent;
 
 /// A trait that can added to any component that allows you to inspect the value in the editor.
 pub trait InspectableComponent {
@@ -1008,8 +1010,12 @@ impl InspectableComponent for MeshRenderer {
 
         let model_reference = self.model().path.clone();
         let model_title = match &model_reference.ref_type {
-            ResourceReferenceType::Cuboid { .. } => "Cuboid".to_string(),
             ResourceReferenceType::Unassigned { .. } => "None".to_string(),
+            ResourceReferenceType::ProcObj(obj) => {
+                match obj {
+                    ProcObj::Cuboid { .. } => "Cuboid".to_string(),
+                }
+            }
             _ => self.handle().label.clone(),
         };
 
@@ -1084,7 +1090,7 @@ impl InspectableComponent for MeshRenderer {
 
                             if ui
                                 .selectable_label(
-                                    matches!(model_reference.ref_type, ResourceReferenceType::Cuboid { .. }),
+                                    matches!(model_reference.ref_type, ResourceReferenceType::ProcObj(ProcObj::Cuboid { .. })),
                                     "Cuboid",
                                 )
                                 .clicked()
@@ -1127,7 +1133,7 @@ impl InspectableComponent for MeshRenderer {
 
             if choose_proc_cuboid {
                 let default_size = match &model_reference.ref_type {
-                    ResourceReferenceType::Cuboid { size_bits } => [
+                    ResourceReferenceType::ProcObj(ProcObj::Cuboid { size_bits }) => [
                         f32::from_bits(size_bits[0]),
                         f32::from_bits(size_bits[1]),
                         f32::from_bits(size_bits[2]),
@@ -1169,7 +1175,7 @@ impl InspectableComponent for MeshRenderer {
                     }
                 }
 
-                if let ResourceReferenceType::Cuboid { size_bits } = &self.model().path.ref_type {
+                if let ResourceReferenceType::ProcObj(ProcObj::Cuboid { size_bits }) = &self.model().path.ref_type {
                     let mut size = [
                         f32::from_bits(size_bits[0]),
                         f32::from_bits(size_bits[1]),
@@ -1544,3 +1550,66 @@ impl InspectableComponent for Camera3D {
         ui.separator();
     }
 }
+
+impl InspectableComponent for UIComponent {
+    fn inspect(
+        &mut self,
+        _entity: &mut Entity,
+        cfg: &mut StaticallyKept,
+        ui: &mut Ui,
+        _undo_stack: &mut Vec<UndoableAction>,
+        _signal: &mut Signal,
+        _label: &mut String,
+    ) {
+        ui.vertical(|ui| {
+            ui.checkbox(&mut self.disabled, "Disable rendering of UI");
+
+            ui.separator();
+
+            let (rect, response) = ui.allocate_exact_size(
+                egui::vec2(ui.available_width(), 72.0),
+                egui::Sense::click(),
+            );
+
+            let fill = if response.hovered() {
+                ui.visuals().widgets.hovered.bg_fill
+            } else {
+                ui.visuals().widgets.inactive.bg_fill
+            };
+
+            ui.painter().rect_filled(rect, 4.0, fill);
+            ui.painter().rect_stroke(
+                rect,
+                4.0,
+                ui.visuals().widgets.inactive.bg_stroke,
+                egui::StrokeKind::Inside,
+            );
+
+            let mut card_ui = ui.new_child(
+                UiBuilder::new()
+                    .layout(egui::Layout::centered_and_justified(egui::Direction::LeftToRight))
+                    .max_rect(rect),
+            );
+
+            if let Some(uri) = self.ui_file.as_uri() {
+                card_ui.label(uri);
+            } else {
+                card_ui.label("Drop .kts file here");
+            }
+
+            let pointer_released = ui.input(|i| i.pointer.any_released());
+            if pointer_released && response.hovered() {
+                if let Some(asset) = cfg.dragged_asset.clone() {
+                    if let Some(uri) = asset.path.as_uri() {
+                        if uri.ends_with(".kts") {
+                            if let Ok(new_ref) = ResourceReference::from_euca_uri(uri) {
+                                self.ui_file = new_ref;
+                            }
+                            cfg.dragged_asset = None;
+                        }
+                    }
+                }
+            }
+        });
+    }
+}
\ No newline at end of file
diff --git a/crates/eucalyptus-editor/src/signal.rs b/crates/eucalyptus-editor/src/signal.rs
index f03b7e3..3007a74 100644
--- a/crates/eucalyptus-editor/src/signal.rs
+++ b/crates/eucalyptus-editor/src/signal.rs
@@ -4,7 +4,7 @@ use dropbear_engine::entity::{MeshRenderer, Transform};
 use dropbear_engine::graphics::{SharedGraphicsContext};
 use dropbear_engine::lighting::{Light as EngineLight, LightComponent};
 use dropbear_engine::model::{LoadedModel, Material, Model, ModelId, MODEL_CACHE};
-use dropbear_engine::procedural::ProcedurallyGeneratedObject;
+use dropbear_engine::procedural::{ProcedurallyGeneratedObject, ProcObj};
 use dropbear_engine::texture::{Texture, TextureWrapMode};
 use dropbear_engine::utils::{relative_path_from_euca, EUCA_SCHEME, ResourceReference, ResourceReferenceType};
 use egui::Align2;
@@ -653,7 +653,7 @@ impl SignalController for Editor {
                             .build_model(graphics_clone.clone(), None, Some("Cuboid"));
 
                         let model = loaded.make_mut();
-                        model.path = ResourceReference::from_reference(ResourceReferenceType::Cuboid { size_bits });
+                        model.path = ResourceReference::from_reference(ResourceReferenceType::ProcObj(ProcObj::Cuboid { size_bits }));
                         loaded.refresh_registry();
                         loaded
                     } else {
@@ -692,7 +692,7 @@ impl SignalController for Editor {
 
                         {
                             let model = loaded_model.make_mut();
-                            model.path = ResourceReference::from_reference(ResourceReferenceType::Cuboid { size_bits });
+                            model.path = ResourceReference::from_reference(ResourceReferenceType::ProcObj(ProcObj::Cuboid { size_bits }));
                             for material in &mut model.materials {
                                 material.set_tint(graphics_clone.as_ref(), [1.0, 1.0, 1.0, 1.0]);
                             }
@@ -775,7 +775,7 @@ impl SignalController for Editor {
 
                 {
                     let model = loaded_model.make_mut();
-                    model.path = ResourceReference::from_reference(ResourceReferenceType::Cuboid { size_bits });
+                    model.path = ResourceReference::from_reference(ResourceReferenceType::ProcObj(ProcObj::Cuboid { size_bits }));
                 }
                 loaded_model.refresh_registry();
 
diff --git a/crates/eucalyptus-editor/src/spawn.rs b/crates/eucalyptus-editor/src/spawn.rs
index 9b1e98c..008568b 100644
--- a/crates/eucalyptus-editor/src/spawn.rs
+++ b/crates/eucalyptus-editor/src/spawn.rs
@@ -8,6 +8,7 @@ use dropbear_engine::lighting::{Light, LightComponent};
 use dropbear_engine::model::{LoadedModel, Material, Model, ModelId};
 use dropbear_engine::texture::Texture;
 use dropbear_engine::utils::{ResourceReference, ResourceReferenceType};
+use dropbear_engine::procedural::ProcObj;
 use eucalyptus_core::camera::CameraComponent;
 use eucalyptus_core::scene::SceneEntity;
 pub(crate) use eucalyptus_core::spawn::{PendingSpawnController, PENDING_SPAWNS};
@@ -296,7 +297,7 @@ async fn load_renderer_from_serialized(
                     .build_model(graphics.clone(), None, Some(&label));
 
                 let model = loaded_model.make_mut();
-                model.path = ResourceReference::from_reference(ResourceReferenceType::Cuboid { size_bits });
+                model.path = ResourceReference::from_reference(ResourceReferenceType::ProcObj(ProcObj::Cuboid { size_bits }));
 
                 loaded_model.refresh_registry();
 
@@ -313,7 +314,7 @@ async fn load_renderer_from_serialized(
                     .await?;
             MeshRenderer::from_handle_with_import_scale(loaded, import_scale)
         }
-        ResourceReferenceType::Cuboid { size_bits } => {
+        ResourceReferenceType::ProcObj(ProcObj::Cuboid { size_bits }) => {
             let size = [
                 f32::from_bits(size_bits[0]),
                 f32::from_bits(size_bits[1]),
@@ -324,7 +325,7 @@ async fn load_renderer_from_serialized(
                 .build_model(graphics.clone(), None, Some(&label));
 
             let model = loaded_model.make_mut();
-            model.path = ResourceReference::from_reference(ResourceReferenceType::Cuboid { size_bits: *size_bits });
+            model.path = ResourceReference::from_reference(ResourceReferenceType::ProcObj(ProcObj::Cuboid { size_bits: *size_bits }));
 
             loaded_model.refresh_registry();
 
diff --git a/crates/kino-gui/Cargo.toml b/crates/kino-gui/Cargo.toml
deleted file mode 100644
index d5ef34c..0000000
--- a/crates/kino-gui/Cargo.toml
+++ /dev/null
@@ -1,13 +0,0 @@
-[package]
-name = "kino-gui"
-version.workspace = true
-edition.workspace = true
-license.workspace = true
-repository.workspace = true
-readme.workspace = true
-
-[dependencies]
-wgpu.workspace = true
-anyhow.workspace = true
-bytemuck.workspace = true
-parking_lot.workspace = true
diff --git a/crates/kino-gui/README.md b/crates/kino-gui/README.md
deleted file mode 100644
index 0eed566..0000000
--- a/crates/kino-gui/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# kino-gui
-
-_kino-gui_ is a 2D UI immediate mode library that is wgpu native and game design oriented. Named after the sap that comes out of a 
-eucalyptus tree, this library can be used to create 2D designs, create HUDs for a 3D map or create menus for 2D contexts. 
-
-This library is simple to use, and designs itself to be "injected" as an overlay with your surface. 
-
-## Note
-It is a heavy WIP, and there are many features still not done yet. 
\ No newline at end of file
diff --git a/crates/kino-gui/src/lib.rs b/crates/kino-gui/src/lib.rs
deleted file mode 100644
index 4714806..0000000
--- a/crates/kino-gui/src/lib.rs
+++ /dev/null
@@ -1,77 +0,0 @@
-use std::hash::{DefaultHasher, Hash, Hasher};
-use parking_lot::Mutex;
-use crate::math::Size;
-use crate::rendering::KinoRenderer;
-
-pub mod rendering;
-pub(crate) mod math;
-pub(crate) mod primitives;
-pub(crate) mod utils;
-
-pub mod prelude {
-    pub use crate::{
-        rendering::KinoRenderer,
-        math::*,
-        primitives::*,
-        WidgetId,
-        Widget,
-    };
-}
-
-pub struct GumContext {
-    screen_size: Size,
-}
-
-impl GumContext {
-    pub fn new() -> Self {
-        Self { screen_size: Size::default() }
-    }
-}
-
-pub struct KinoUICommandBuffer {
-    contents: Mutex<Vec<Box<dyn Widget>>>,
-}
-
-impl KinoUICommandBuffer {
-    pub(crate) fn new() -> Self {
-        Self {
-            contents: Mutex::new(Vec::new()),
-        }
-    }
-
-    /// Drains the [self.contents] and sends back to the renderer
-    pub(crate) fn process<'a>(&self) -> Vec<Box<dyn Widget>> {
-        let mut contents = self.contents.lock();
-        contents.drain(..).collect()
-    }
-
-    pub fn add<T: Widget + 'static>(&self, widget: T) {
-        self.contents.lock().push(Box::new(widget));
-    }
-}
-
-pub trait Widget {
-    fn id(&self) -> WidgetId;
-    fn draw<'a>(&mut self, renderer: &KinoRenderer, pass: &mut wgpu::RenderPass<'a>);
-}
-
-#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
-pub struct WidgetId(u64);
-
-impl WidgetId {
-    pub fn new(id: u64) -> Self {
-        Self(id)
-    }
-
-    pub fn as_u64(&self) -> u64 {
-        self.0
-    }
-}
-
-impl Into<WidgetId> for String {
-    fn into(self) -> WidgetId {
-        let mut hasher = DefaultHasher::new();
-        self.hash(&mut hasher);
-        WidgetId(hasher.finish())
-    }
-}
\ No newline at end of file
diff --git a/crates/kino-gui/src/math.rs b/crates/kino-gui/src/math.rs
deleted file mode 100644
index 8c2b8ee..0000000
--- a/crates/kino-gui/src/math.rs
+++ /dev/null
@@ -1,106 +0,0 @@
-#[derive(Debug, Clone, PartialEq)]
-pub struct Rect {
-    /// States the left most corner of the rectangle in reference to the window
-    pub initial: Vector2,
-    /// The width and height that extends from the initial point, with the width extending
-    /// left and the height extending down. 
-    pub size: Size,
-}
-
-#[derive(Debug, Copy, Clone, PartialEq)]
-pub struct Vector2 {
-    pub x: f32,
-    pub y: f32,
-}
-
-impl Into<Size> for Vector2 {
-    fn into(self) -> Size {
-        Size {
-            width: self.x,
-            height: self.y,
-        }
-    }
-}
-
-impl Into<[f32; 2]> for Vector2 {
-    fn into(self) -> [f32; 2] {
-        [self.x, self.y]
-    }
-}
-
-impl Into<Vector2> for [f32; 2] {
-    fn into(self) -> Vector2 {
-        Vector2 {
-            x: self[0],
-            y: self[1],
-        }
-    }
-}
-
-#[derive(Debug, Copy, Clone, PartialEq)]
-pub struct Size {
-    pub width: f32,
-    pub height: f32
-}
-
-impl Default for Size {
-    fn default() -> Self {
-        Self {
-            // cant be zero otherwise wgpu panics
-            width: 800.0,
-            height: 600.0,
-        }
-    }
-}
-
-impl Into<Vector2> for Size {
-    fn into(self) -> Vector2 {
-        Vector2 {
-            x: self.width,
-            y: self.height,
-        }
-    }
-}
-
-impl Into<[f32; 2]> for Size {
-    fn into(self) -> [f32; 2] {
-        [self.width, self.height]
-    }
-}
-
-impl Into<Size> for [f32; 2] {
-    fn into(self) -> Size {
-        Size {
-            width: self[0],
-            height: self[1],
-        }
-    }
-}
-
-#[derive(Debug, Clone, PartialEq)]
-pub struct Colour {
-    pub r: f32,
-    pub g: f32,
-    pub b: f32,
-    pub a: f32,
-}
-
-pub fn create_orthographic_projection(
-    left: f32,
-    right: f32,
-    bottom: f32,
-    top: f32,
-    near: f32,
-    far: f32,
-) -> [[f32; 4]; 4] {
-    let width = right - left;
-    let height = top - bottom;
-    let depth = far - near;
-
-    [
-        [2.0 / width, 0.0, 0.0, 0.0],
-        [0.0, 2.0 / height, 0.0, 0.0],
-        [0.0, 0.0, -2.0 / depth, 0.0],
-        [-(right + left) / width, -(bottom + top) / height, -(far + near) / depth, 1.0],
-    ]
-}
\ No newline at end of file
diff --git a/crates/kino-gui/src/primitives/mod.rs b/crates/kino-gui/src/primitives/mod.rs
deleted file mode 100644
index 4f68ece..0000000
--- a/crates/kino-gui/src/primitives/mod.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub mod shapes;
\ No newline at end of file
diff --git a/crates/kino-gui/src/primitives/shapes.rs b/crates/kino-gui/src/primitives/shapes.rs
deleted file mode 100644
index 70d681b..0000000
--- a/crates/kino-gui/src/primitives/shapes.rs
+++ /dev/null
@@ -1,120 +0,0 @@
-use wgpu::RenderPass;
-use wgpu::util::DeviceExt;
-use crate::math::{create_orthographic_projection, Colour, Size, Vector2};
-use crate::{Widget, WidgetId};
-use crate::rendering::{Globals, KinoRenderer};
-use crate::utils::UniformBuffer;
-
-pub struct Rectangle {
-    pub id: WidgetId,
-    pub initial: Vector2,
-    pub size: Size,
-    pub fill_colour: Colour,
-
-    globals_uniform: Option<UniformBuffer<Globals>>,
-    vertex_buffer: Option<wgpu::Buffer>,
-}
-
-impl Rectangle {
-    pub fn new(id: WidgetId, initial: Vector2, size: Size, fill_colour: Colour) -> Self {
-        Self {
-            id,
-            initial,
-            size,
-            fill_colour,
-            globals_uniform: None,
-            vertex_buffer: None,
-        }
-    }
-
-    pub(crate) fn create_vertex_buffer(&self, device: &wgpu::Device) -> wgpu::Buffer {
-        let x = self.initial.x;
-        let y = self.initial.y;
-        let w = self.size.width;
-        let h = self.size.height;
-        let c = [
-            self.fill_colour.r,
-            self.fill_colour.g,
-            self.fill_colour.b,
-            self.fill_colour.a,
-        ];
-
-        #[repr(C)]
-        #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
-        struct Vertex {
-            position: [f32; 3],
-            fill_colour: [f32; 4],
-        }
-
-        let vertices: [Vertex; 6] = [
-            Vertex { position: [x, y, 0.0], fill_colour: c },
-            Vertex { position: [x + w, y, 0.0], fill_colour: c },
-            Vertex { position: [x + w, y + h, 0.0], fill_colour: c },
-            Vertex { position: [x, y, 0.0], fill_colour: c },
-            Vertex { position: [x + w, y + h, 0.0], fill_colour: c },
-            Vertex { position: [x, y + h, 0.0], fill_colour: c },
-        ];
-
-        device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
-            label: Some("rectangle vertex buffer"),
-            contents: bytemuck::cast_slice(&vertices),
-            usage: wgpu::BufferUsages::VERTEX,
-        })
-    }
-}
-
-impl Widget for Rectangle {
-    fn id(&self) -> WidgetId {
-        self.id
-    }
-
-    fn draw<'a>(&mut self, renderer: &KinoRenderer, pass: &mut RenderPass<'a>) {
-        if self.globals_uniform.is_none() {
-            let globals = Globals::new(
-                create_orthographic_projection(
-                    0.0,
-                    renderer.context.screen_size.width,
-                    renderer.context.screen_size.height,
-                    0.0,
-                    -1.0,
-                    1.0
-                ),
-            [
-                renderer.context.screen_size.width,
-                renderer.context.screen_size.height,
-                ],
-            );
-
-            self.globals_uniform = Some(UniformBuffer::new(
-                &renderer.render.device,
-                Some(globals),
-                Some("rectangle globals uniform"),
-            ));
-        }
-
-        if self.vertex_buffer.is_none() {
-            self.vertex_buffer = Some(self.create_vertex_buffer(&renderer.render.device));
-        }
-
-        let globals_bind_group = renderer.render.device.create_bind_group(&wgpu::BindGroupDescriptor {
-            label: Some("rectangle globals bind group"),
-            layout: &renderer.render.globals_uniform_layout,
-            entries: &[
-                wgpu::BindGroupEntry {
-                    binding: 0,
-                    resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
-                        buffer: self.globals_uniform.as_ref().unwrap().buffer(),
-                        offset: 0,
-                        size: None,
-                    }),
-                }
-            ],
-        });
-
-        pass.set_pipeline(&renderer.render.pipeline);
-        pass.set_bind_group(0, &globals_bind_group, &[]);
-        pass.set_vertex_buffer(0, self.vertex_buffer.as_ref().unwrap().slice(..));
-
-        pass.draw(0..6, 0..1);
-    }
-}
\ No newline at end of file
diff --git a/crates/kino-gui/src/rendering.rs b/crates/kino-gui/src/rendering.rs
deleted file mode 100644
index bbb9ce3..0000000
--- a/crates/kino-gui/src/rendering.rs
+++ /dev/null
@@ -1,195 +0,0 @@
-use std::num::NonZeroU64;
-use std::sync::Arc;
-use anyhow::Context;
-use wgpu::{include_wgsl, BindGroupLayoutEntry, ShaderStages, VertexAttribute, VertexStepMode};
-use crate::{GumContext, KinoUICommandBuffer};
-use crate::math::Size;
-
-pub struct KinoRenderer {
-    pub(crate) context: GumContext,
-    pub(crate) render: KinoRenderPipeline,
-    pub(crate) ui: Arc<KinoUICommandBuffer>,
-}
-
-pub(crate) struct KinoRenderPipeline {
-    pub(crate) globals_uniform_layout: wgpu::BindGroupLayout,
-    pub(crate) pipeline: wgpu::RenderPipeline,
-    pub(crate) device: Arc<wgpu::Device>,
-    pub(crate) queue: Arc<wgpu::Queue>,
-}
-
-impl KinoRenderer {
-    pub fn new(
-        device: Arc<wgpu::Device>,
-        queue: Arc<wgpu::Queue>,
-        texture_format: wgpu::TextureFormat,
-    ) -> anyhow::Result<Self> {
-        let shader = device.create_shader_module(include_wgsl!("shaders/primitive.wgsl"));
-
-        let globals_uniform_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
-            label: Some("kino globals uniform"),
-            entries: &[
-                BindGroupLayoutEntry {
-                    binding: 0,
-                    visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
-                    ty: wgpu::BindingType::Buffer {
-                        ty: wgpu::BufferBindingType::Uniform,
-                        has_dynamic_offset: false,
-                        min_binding_size: None,
-                    },
-                    count: None,
-                }
-            ],
-        });
-
-        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
-            label: Some("kino render pipeline layout"),
-            bind_group_layouts: &[
-                &globals_uniform_layout
-            ],
-            push_constant_ranges: &[],
-        });
-
-        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
-            label: Some("kino render pipeline"),
-            layout: Some(&pipeline_layout),
-            vertex: wgpu::VertexState {
-                module: &shader,
-                entry_point: Some("vs_main"),
-                compilation_options: Default::default(),
-                buffers: &[
-                    VertexInput::desc()
-                ],
-            },
-            primitive: Default::default(),
-            depth_stencil: None,
-            multisample: Default::default(),
-            fragment: Some(wgpu::FragmentState {
-                module: &shader,
-                entry_point: Some("fs_main"),
-                compilation_options: Default::default(),
-                targets: &[
-                    Some(wgpu::ColorTargetState {
-                        format: texture_format,
-                        blend: Some(wgpu::BlendState::ALPHA_BLENDING),
-                        write_mask: Default::default(),
-                    })
-                ],
-            }),
-            multiview: None,
-            cache: None,
-        });
-
-        Ok(Self {
-            context: GumContext::new(),
-            render: KinoRenderPipeline {
-                globals_uniform_layout,
-                pipeline,
-                device,
-                queue,
-            },
-            ui: Arc::new(KinoUICommandBuffer::new()),
-        })
-    }
-
-    pub fn get_ui(&self) -> Arc<KinoUICommandBuffer> {
-        self.ui.clone()
-    }
-
-    /// Uses [self.render] in the backend, creates an encoder with the device provided, and
-    /// renders the content.
-    pub fn render_without_encoder(
-        &mut self,
-        view: &wgpu::TextureView,
-        size_to_render: Size,
-    ) {
-        let mut encoder = self.render.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
-            label: Some("kino render encoder"),
-        });
-
-        self.render(&mut encoder, view, size_to_render);
-
-        self.render.queue.submit(std::iter::once(encoder.finish()));
-    }
-
-    /// Renders the content onto the provided texture/view.
-    pub fn render(
-        &mut self,
-        encoder: &mut wgpu::CommandEncoder,
-        view: &wgpu::TextureView,
-        size_to_render: Size,
-    ) {
-        let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
-            label: Some("kino render pass"),
-            color_attachments: &[
-                Some(wgpu::RenderPassColorAttachment {
-                    view,
-                    depth_slice: None,
-                    resolve_target: None,
-                    ops: wgpu::Operations {
-                        load: wgpu::LoadOp::Load,
-                        store: wgpu::StoreOp::Store,
-                    },
-                })
-            ],
-            depth_stencil_attachment: None,
-            timestamp_writes: None,
-            occlusion_query_set: None,
-        });
-
-        self.context.screen_size = size_to_render;
-        
-        let mut contents = self.ui.process();
-        for mut widget in contents.drain(..) {
-            widget.draw(&self, &mut pass);
-        }
-    }
-}
-
-pub struct VertexInput {
-    position: [f32; 3],
-    fill_colour: [f32; 4],
-}
-
-impl VertexInput {
-    pub fn desc() -> wgpu::VertexBufferLayout<'static> {
-        wgpu::VertexBufferLayout {
-            array_stride: 0,
-            step_mode: VertexStepMode::Vertex,
-            attributes: &[
-                // position
-                VertexAttribute {
-                    format: wgpu::VertexFormat::Float32x3,
-                    offset: 0,
-                    shader_location: 0,
-                },
-
-                // fill_colour
-                VertexAttribute {
-                    format: wgpu::VertexFormat::Float32x4,
-                    offset: size_of::<[f32; 3]>() as u64,
-                    shader_location: 1,
-                }
-            ],
-        }
-    }
-}
-
-#[repr(C)]
-#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
-#[derive(Default)]
-pub struct Globals {
-    pub proj: [[f32; 4]; 4],
-    pub screen_size: [f32; 2],
-    pub _padding: [f32; 2],
-}
-
-impl Globals {
-    pub fn new(proj: [[f32; 4]; 4], screen_size: [f32; 2]) -> Self {
-        Self {
-            proj,
-            screen_size,
-            _padding: [0.0; 2],
-        }
-    }
-}
\ No newline at end of file
diff --git a/crates/kino-gui/src/shaders/primitive.wgsl b/crates/kino-gui/src/shaders/primitive.wgsl
deleted file mode 100644
index 3d250e3..0000000
--- a/crates/kino-gui/src/shaders/primitive.wgsl
+++ /dev/null
@@ -1,30 +0,0 @@
-struct Globals {
-    proj: mat4x4<f32>,
-    screen_size: vec2<f32>,
-}
-
-@group(0) @binding(0)
-var<uniform> globals: Globals;
-
-struct VertexInput {
-    @location(0) position: vec3<f32>,
-    @location(1) fill_colour: vec4<f32>,
-}
-
-struct VertexOutput {
-    @builtin(position) position: vec4<f32>,
-    @location(0) colour: vec4<f32>,
-}
-
-@vertex
-fn vs_main(in: VertexInput) -> VertexOutput {
-    var out: VertexOutput;
-    out.position = globals.proj * vec4<f32>(in.position, 1.0);
-    out.colour = in.fill_colour;
-    return out;
-}
-
-@fragment
-fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
-    return in.colour;
-}
\ No newline at end of file
diff --git a/crates/kino-gui/src/utils.rs b/crates/kino-gui/src/utils.rs
deleted file mode 100644
index 9415989..0000000
--- a/crates/kino-gui/src/utils.rs
+++ /dev/null
@@ -1,35 +0,0 @@
-use wgpu::util::{BufferInitDescriptor, DeviceExt};
-
-pub struct UniformBuffer<T> {
-    pub inner: T,
-    buffer: wgpu::Buffer,
-}
-
-impl<T: bytemuck::Pod + Default> UniformBuffer<T> {
-    pub fn new(device: &wgpu::Device, uniform: Option<T>, label: Option<&str>) -> Self {
-        let uniform = if let Some(uniform) = uniform {
-            uniform
-        } else {
-            T::default()
-        };
-
-        let buffer = device.create_buffer_init(&BufferInitDescriptor {
-            label,
-            contents: bytemuck::cast_slice(&[uniform]),
-            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
-        });
-
-        Self {
-            inner: uniform,
-            buffer,
-        }
-    }
-
-    pub fn write(&self, queue: &wgpu::Queue, uniform: T) {
-        queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(&[uniform]));
-    }
-    
-    pub fn buffer(&self) -> &wgpu::Buffer {
-        &self.buffer
-    }
-}
diff --git a/crates/redback-runtime/Cargo.toml b/crates/redback-runtime/Cargo.toml
index acbc5b7..2456a7a 100644
--- a/crates/redback-runtime/Cargo.toml
+++ b/crates/redback-runtime/Cargo.toml
@@ -9,7 +9,6 @@ readme = "README.md"
 [dependencies]
 dropbear-engine = { path = "../dropbear-engine" }
 eucalyptus-core = { path = "../eucalyptus-core", features = ["runtime"], default-features = false }
-kino-gui = { path = "../kino-gui" }
 
 anyhow.workspace = true
 log.workspace = true
@@ -32,6 +31,9 @@ futures.workspace = true
 glam.workspace = true
 wgpu.workspace = true
 egui.workspace = true
+yakui.workspace = true
+yakui-wgpu.workspace = true
+egui_extras.workspace = true
 
 [features]
 debug = []
diff --git a/crates/redback-runtime/src/input.rs b/crates/redback-runtime/src/input.rs
index d127717..4f2e8a4 100644
--- a/crates/redback-runtime/src/input.rs
+++ b/crates/redback-runtime/src/input.rs
@@ -5,6 +5,7 @@ use winit::event_loop::ActiveEventLoop;
 use winit::keyboard::KeyCode;
 use dropbear_engine::input::{Controller, Keyboard, Mouse};
 use crate::PlayMode;
+use eucalyptus_core::ui::UI_CONTEXT;
 
 impl Keyboard for PlayMode {
     fn key_down(&mut self, key: KeyCode, _event_loop: &ActiveEventLoop) {
@@ -31,14 +32,57 @@ impl Mouse for PlayMode {
         self.input_state.mouse_delta = delta;
         self.input_state.mouse_pos = (position.x, position.y);
         self.input_state.last_mouse_pos = Some(<(f64, f64)>::from(position));
+
+        UI_CONTEXT.with(|ctx| {
+            let yak = ctx.borrow();
+            let mut yakui = yak.yakui_state.lock();
+            let relative_x = (position.x as f32) - self.viewport_offset.0;
+            let relative_y = (position.y as f32) - self.viewport_offset.1;
+            
+            yakui.handle_event(yakui::event::Event::CursorMoved(Some(yakui::geometry::Vec2::new(relative_x, relative_y))));
+        });
     }
 
     fn mouse_down(&mut self, button: MouseButton) {
         self.input_state.mouse_button.insert(button);
+        
+        UI_CONTEXT.with(|ctx| {
+            let yak = ctx.borrow();
+            let mut yakui = yak.yakui_state.lock();
+            let btn = match button {
+                MouseButton::Left => Some(yakui::input::MouseButton::One),
+                MouseButton::Right => Some(yakui::input::MouseButton::Two),
+                MouseButton::Middle => Some(yakui::input::MouseButton::Three),
+                _ => None,
+            };
+            if let Some(b) = btn {
+                yakui.handle_event(yakui::event::Event::MouseButtonChanged {
+                    button: b,
+                    down: true,
+                });
+            }
+        });
     }
 
     fn mouse_up(&mut self, button: MouseButton) {
         self.input_state.mouse_button.remove(&button);
+        
+        UI_CONTEXT.with(|ctx| {
+            let yak = ctx.borrow();
+            let mut yakui = yak.yakui_state.lock();
+            let btn = match button {
+                MouseButton::Left => Some(yakui::input::MouseButton::One),
+                MouseButton::Right => Some(yakui::input::MouseButton::Two),
+                MouseButton::Middle => Some(yakui::input::MouseButton::Three),
+                _ => None,
+            };
+            if let Some(b) = btn {
+                yakui.handle_event(yakui::event::Event::MouseButtonChanged {
+                    button: b,
+                    down: false,
+                });
+            }
+        });
     }
 }
 
diff --git a/crates/redback-runtime/src/lib.rs b/crates/redback-runtime/src/lib.rs
index a857bd8..6520560 100644
--- a/crates/redback-runtime/src/lib.rs
+++ b/crates/redback-runtime/src/lib.rs
@@ -12,7 +12,7 @@ use eucalyptus_core::physics::collider::shader::ColliderInstanceRaw;
 use eucalyptus_core::physics::collider::{ColliderShapeKey, WireframeGeometry};
 use futures::executor;
 use hecs::{Entity, World};
-use dropbear_engine::future::FutureHandle;
+use dropbear_engine::future::{FutureHandle, FutureQueue};
 use dropbear_engine::graphics::SharedGraphicsContext;
 use dropbear_engine::scene::SceneCommand;
 use eucalyptus_core::input::InputState;
@@ -85,7 +85,6 @@ pub struct PlayMode {
     main_pipeline: Option<MainRenderPipeline>,
     shader_globals: Option<GlobalsUniform>,
     collider_wireframe_pipeline: Option<ColliderWireframePipeline>,
-    kino_renderer: Option<kino_gui::prelude::KinoRenderer>,
 
     initial_scene: Option<String>,
     current_scene: Option<String>,
@@ -109,6 +108,7 @@ pub struct PlayMode {
 
     collider_wireframe_geometry_cache: HashMap<ColliderShapeKey, WireframeGeometry>,
     collider_instance_buffer: Option<ResizableBuffer<ColliderInstanceRaw>>,
+    viewport_offset: (f32, f32),
 }
 
 impl PlayMode {
@@ -150,10 +150,10 @@ impl PlayMode {
             collider_wireframe_pipeline: None,
             collider_wireframe_geometry_cache: HashMap::new(),
             collider_instance_buffer: None,
+            viewport_offset: (0.0, 0.0),
             collision_event_receiver: Some(ce_r),
             collision_force_event_receiver: Some(cfe_r),
             event_collector,
-            kino_renderer: None,
         };
 
         log::debug!("Created new play mode instance");
@@ -166,20 +166,6 @@ impl PlayMode {
         self.main_pipeline = Some(MainRenderPipeline::new(graphics.clone()));
         self.shader_globals = Some(GlobalsUniform::new(graphics.clone(), Some("runtime shader globals")));
         self.collider_wireframe_pipeline = Some(ColliderWireframePipeline::new(graphics.clone()));
-        
-        match kino_gui::prelude::KinoRenderer::new(
-            graphics.device.clone(),
-            graphics.queue.clone(),
-            graphics.surface_format,
-        ) {
-            Ok(renderer) => {
-                self.kino_renderer = Some(renderer);
-                log::debug!("KinoRenderer initialized successfully");
-            }
-            Err(e) => {
-                log::error!("Failed to initialize KinoRenderer: {}", e);
-            }
-        }
     }
 
     fn reload_scripts_for_current_world(&mut self) {
@@ -257,7 +243,7 @@ impl PlayMode {
         let graphics_cloned = graphics.clone();
         let component_registry = self.component_registry.clone();
 
-        let handle = graphics.future_queue.push(async move {
+        let handle = FutureQueue::push(&graphics.future_queue, async move {
             let mut temp_world = World::new();
             let load_status = scene_to_load.load_into_world(
                 &mut temp_world,
@@ -278,7 +264,7 @@ impl PlayMode {
 
                     v
                 }
-                Err(e) => {panic!("Failed to load scene [{}]: {}", scene_to_load.scene_name, e);}
+                Err(e) => { panic!("Failed to load scene [{}]: {}", scene_to_load.scene_name, e); }
             }
         });
 
diff --git a/crates/redback-runtime/src/scene.rs b/crates/redback-runtime/src/scene.rs
index 39eb8d2..a95edbe 100644
--- a/crates/redback-runtime/src/scene.rs
+++ b/crates/redback-runtime/src/scene.rs
@@ -12,6 +12,10 @@ use hecs::Entity;
 use wgpu::{Color};
 use wgpu::util::DeviceExt;
 use winit::event_loop::ActiveEventLoop;
+use yakui::column;
+use yakui::widgets::Button;
+use yakui::widgets::{Layer, Pad};
+use yakui_wgpu::SurfaceInfo;
 use dropbear_engine::camera::Camera;
 use dropbear_engine::buffer::ResizableBuffer;
 use dropbear_engine::entity::{EntityTransform, MeshRenderer, Transform};
@@ -20,6 +24,7 @@ use dropbear_engine::lighting::{Light, LightComponent};
 use dropbear_engine::lighting::MAX_LIGHTS;
 use dropbear_engine::model::{DrawLight, DrawModel, ModelId, MODEL_CACHE};
 use dropbear_engine::scene::{Scene, SceneCommand};
+use dropbear_engine::texture::Texture;
 use eucalyptus_core::camera::CameraComponent;
 use eucalyptus_core::command::CommandBufferPoller;
 use eucalyptus_core::hierarchy::{EntityTransformExt, Parent};
@@ -31,6 +36,7 @@ use eucalyptus_core::states::SCENES;
 use eucalyptus_core::scene::loading::{IsSceneLoaded, SceneLoadResult, SCENE_LOADER};
 use crate::{PlayMode};
 use eucalyptus_core::physics::collider::shader::create_wireframe_geometry;
+use eucalyptus_core::ui::UI_CONTEXT;
 
 impl Scene for PlayMode {
     fn load(&mut self, graphics: Arc<SharedGraphicsContext>) {
@@ -396,7 +402,8 @@ impl Scene for PlayMode {
                 if !p.is_everything_loaded() && p.is_first_scene {
                     // todo: change from label to "splashscreen"
                     ui.centered_and_justified(|ui| {
-                        ui.label("Loading scene...");
+                        egui_extras::install_image_loaders(&graphics.get_egui_context());
+                        ui.image(egui::include_image!("../../../resources/eucalyptus-editor.png"))
                     });
                     return;
                 }
@@ -438,6 +445,8 @@ impl Scene for PlayMode {
                         egui::vec2(display_width, display_height),
                     );
 
+                    self.viewport_offset = (image_rect.min.x, image_rect.min.y);
+
                     ui.allocate_exact_size(available_size, egui::Sense::hover());
 
                     ui.scope_builder(egui::UiBuilder::new().max_rect(image_rect), |ui| {
@@ -446,6 +455,34 @@ impl Scene for PlayMode {
                             size: egui::vec2(display_width, display_height),
                         }));
                     });
+
+                    // overlay
+                    UI_CONTEXT.with(|yakui_cell| {
+                        let yak = yakui_cell.borrow();
+                        let mut yakui = yak.yakui_state.lock();
+
+                        yakui.set_surface_size(yakui::geometry::Vec2::new(display_width, display_height));
+                        yakui.start();
+
+                        let to_render = yak.to_render.lock().drain(..).collect::<Vec<_>>();
+
+                        Layer::new().show(|| {
+                            column(|| {
+                                for f in to_render {
+                                    f()
+                                }
+
+                                let button_response = Button::styled("My Button")
+                                    .padding(Pad::all(10.0))
+                                    .show();
+                                if button_response.clicked {
+                                    println!("This is clicked!");
+                                }
+                            });
+                        });
+
+                        yakui.finish();
+                    });
                 } else {
                     log::warn!("No such camera exists in the world");
                 }
@@ -816,46 +853,26 @@ impl Scene for PlayMode {
                     }
                 }
             }
+
             if let Err(e) = encoder.submit(graphics.clone()) {
                 log_once::error_once!("{}", e);
             }
-        }
 
-        if let Some(kino_renderer) = &mut self.kino_renderer {
-            let mut encoder = CommandEncoder::new(graphics.clone(), Some("kino ui render encoder"));
-            
-            let commands = eucalyptus_core::ui::UI_COMMAND_BUFFER.drain_commands();
-            let ui_buffer = kino_renderer.get_ui();
-
-            for command in commands {
-                match command {
-                    eucalyptus_core::ui::UiCommand::Rect { id, initial, size, fill, .. } => {
-                        let rect = kino_gui::prelude::shapes::Rectangle::new(
-                            kino_gui::prelude::WidgetId::new(id),
-                            initial,
-                            size,
-                            fill
-                        );
-                        ui_buffer.add(rect);
+            UI_CONTEXT.with(|v| {
+                let commands = graphics.yakui_renderer.lock().paint(
+                    &mut v.borrow().yakui_state.lock(),
+                    &graphics.device,
+                    &graphics.queue,
+                    SurfaceInfo {
+                        format: Texture::TEXTURE_FORMAT,
+                        sample_count: 1,
+                        color_attachment: &graphics.viewport_texture.view,
+                        resolve_target: None,
                     }
-                    _ => {}
-                }
-            }
-
-            let screen_size = kino_gui::prelude::Size {
-                width: graphics.viewport_texture.size.width as f32,
-                height: graphics.viewport_texture.size.height as f32,
-            };
-
-            kino_renderer.render(
-                &mut *encoder,
-                &graphics.viewport_texture.view,
-                screen_size,
-            );
+                );
 
-            if let Err(e) = encoder.submit(graphics.clone()) {
-                log_once::error_once!("Failed to submit kino renderer: {}", e);
-            }
+                graphics.queue.submit([commands]);
+            });
         }
     }
 
diff --git a/docs/player_hud.kts b/docs/player_hud.kts
new file mode 100644
index 0000000..a2add67
--- /dev/null
+++ b/docs/player_hud.kts
@@ -0,0 +1,75 @@
+package com.dropbear.sample
+
+DynamicBoundingBox {
+    Circle {
+        name = "status_backing"
+
+        align {
+            horizontal = Alignment.Left + 10
+            vertical = Alignment.Centered
+        }
+
+        radius = 40.0
+
+        Image {
+            name = "status_image"
+
+            when (Player.playerState) {
+                PlayerState.Gas -> {
+                    resource = "euca://player/status/gas.png"
+                }
+                PlayerState.Solid -> {
+                    resource = "euca://player/status/solid.png"
+                }
+                PlayerState.Liquid -> {
+                    resource = "euca://player/status/liquid.png"
+                }
+            }
+
+            align = Align.default()
+        }
+    }
+
+    bar("health_bar", Align.Center + 20.0)
+
+    bar("energy_bar", Align.Center - 20.0)
+}
+
+bar(val objectName: String, align: Align = Align.default()): DynamicBoundingBox {
+    Rectangle {
+        name = objectName + " backing"
+
+        size = Vector2d(30.0, 10.0)
+
+        align = align
+
+        style {
+            fill = null
+            stroke = Stroke(
+                colour = Colour.BLACK,
+                width = 1.0
+            )
+        }
+    }
+
+    Rectangle {
+        name = objectName + " fill"
+
+        size = Vector2d(Player.health.percentage() * 30.0, 10.0)
+
+        align = align
+
+        style {
+            fill = Fill(
+                colour = when (Player.currentZone) {
+                    CurrentZone.Freezing -> Colour.DARK_BLUE
+                    CurrentZone.Cold -> Colour.LIGHT_BLUE
+                    CurrentZone.Normal -> Colour.LIGHT_GREY
+                    CurrentZone.Hot -> Colour.ORANGE
+                    CurrentZone.Boiling -> Colour.SCARLET_RED
+                }
+            )
+            stroke = null
+        }
+    }
+}
\ No newline at end of file
diff --git a/scripting/commonMain/kotlin/com/dropbear/ui/Align.kt b/scripting/commonMain/kotlin/com/dropbear/ui/Align.kt
new file mode 100644
index 0000000..af76265
--- /dev/null
+++ b/scripting/commonMain/kotlin/com/dropbear/ui/Align.kt
@@ -0,0 +1,6 @@
+package com.dropbear.ui
+
+class Align {
+    var horizontal: Alignment = Alignment.Center
+    var vertical: Alignment = Alignment.Center
+}
\ No newline at end of file
diff --git a/scripting/commonMain/kotlin/com/dropbear/ui/Alignment.kt b/scripting/commonMain/kotlin/com/dropbear/ui/Alignment.kt
new file mode 100644
index 0000000..efd2fbd
--- /dev/null
+++ b/scripting/commonMain/kotlin/com/dropbear/ui/Alignment.kt
@@ -0,0 +1,10 @@
+package com.dropbear.ui
+
+enum class Alignment {
+    Left,
+    Center,
+    Right,
+
+    Top,
+    Bottom
+}
\ No newline at end of file
diff --git a/scripting/commonMain/kotlin/com/dropbear/ui/Response.kt b/scripting/commonMain/kotlin/com/dropbear/ui/Response.kt
deleted file mode 100644
index b4ddac7..0000000
--- a/scripting/commonMain/kotlin/com/dropbear/ui/Response.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-package com.dropbear.ui
-
-import com.dropbear.input.MouseButton
-import com.dropbear.math.Vector2d
-import com.dropbear.utils.ID
-
-/**
- * The response given by an element that is drawn.
- */
-class Response(
-    val elementId: ID,
-    private val ui: Ui
-) {
-    fun clicked(): Boolean = ui.wasClicked(elementId)
-    fun clickedBy(button: MouseButton): Boolean = TODO("Not implemented yet")
-    fun secondaryClicked(): Boolean = TODO("Not implemented yet")
-    fun longTouched(): Boolean = TODO("Not implemented yet")
-    fun middleClicked(): Boolean = TODO("Not implemented yet")
-    fun doubleClicked(): Boolean = TODO("Not implemented yet")
-    fun tripleClicked(): Boolean = TODO("Not implemented yet")
-    fun doubleClickedBy(button: MouseButton): Boolean = TODO("Not implemented yet")
-    fun tripleClickedBy(button: MouseButton): Boolean = TODO("Not implemented yet")
-    fun clickedWithOpenInBackground(): Boolean = TODO("Not implemented yet")
-    fun clickedElsewhere(): Boolean = TODO("Not implemented yet")
-    fun enabled(): Boolean = TODO("Not implemented yet")
-    fun hovered(): Boolean = TODO("Not implemented yet")
-    fun hasFocus(): Boolean = TODO("Not implemented yet")
-    fun gainedFocus(): Boolean = TODO("Not implemented yet")
-    fun lostFocus() {}
-    fun requestFocus() {}
-    fun surrenderFocus() {}
-    fun dragStarted(): Boolean = TODO("Not implemented yet")
-    fun dragStartedBy(button: MouseButton): Boolean = TODO("Not implemented yet")
-    fun dragged(): Boolean = TODO("Not implemented yet")
-    fun draggedBy(button: MouseButton): Boolean = TODO("Not implemented yet")
-    fun dragStopped(): Boolean = TODO("Not implemented yet")
-    fun dragStoppedBy(button: MouseButton): Boolean = TODO("Not implemented yet")
-    fun dragDelta(): Vector2d = TODO("Not implemented yet")
-    fun totalDragDelta(): Vector2d = TODO("Not implemented yet")
-    fun dragMotion(): Vector2d = TODO("Not implemented yet")
-
-    // todo: payload
-
-    fun interactPointerPos(): Vector2d? = TODO("Not implemented yet")
-    fun hoverPos(): Vector2d = TODO("Not implemented yet")
-    fun isPointerButtonDownOn(): Boolean = TODO("Not implemented yet")
-    fun changed(): Boolean = TODO("Not implemented yet")
-    fun markChanged() {}
-    fun shouldClose(): Boolean = TODO("Not implemented yet")
-    fun setClose() {}
-    fun onHoverUi(ui: (Ui) -> Unit): Response = TODO("Not implemented yet")
-    fun onDisabledHoverUi(ui: (Ui) -> Unit) : Response = TODO("Not implemented yet")
-    fun onHoverUiAtPointer(ui: (Ui) -> Unit) : Response = TODO("Not implemented yet")
-    fun showTooltipUi(ui: (Ui) -> Unit) {}
-    fun showTooltipText(text: String) {}
-    fun isTooltipOpen(): Boolean = TODO("Not implemented yet")
-    fun onHoverTextAtPointer(text: String) {}
-    fun onHoverText(text: String) {}
-    fun onDisabledHoverText(text: String) {}
-    fun highlight(): Boolean = TODO("Not implemented yet")
-    fun interact(sense: Sense): Response = TODO("Not implemented yet")
-}
\ No newline at end of file
diff --git a/scripting/commonMain/kotlin/com/dropbear/ui/Sense.kt b/scripting/commonMain/kotlin/com/dropbear/ui/Sense.kt
deleted file mode 100644
index f247beb..0000000
--- a/scripting/commonMain/kotlin/com/dropbear/ui/Sense.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-package com.dropbear.ui
-
-/**
- * What sort of interaction is a widget sensitive to?
- */
-class Sense(val bit: Int) {
-    companion object {
-        const val HOVER = 0
-
-        /**
-         * Buttons, sliders, windows, …
-         */
-        const val CLICK = 1 shl 0
-
-        /**
-         * Sliders, windows, scroll bars, scroll areas, …
-         */
-        const val DRAG = 1 shl 1
-
-        /**
-         * This widget wants focus.
-         *
-         * Anything interactive + labels that can be focused for the benefit of screen readers.
-         */
-        const val FOCUSABLE = 1 shl 2
-
-        /**
-         * Senses no clicks or drags. Only senses mouse hover.
-         */
-        fun hover(): Sense {
-            return Sense(HOVER)
-        }
-
-        /**
-         * Senses no clicks or drags, but can be focused with the keyboard.
-         *
-         * Used for labels that can be focused for the benefit of screen readers.
-         */
-        fun focusableNonInteractive(): Sense {
-            return Sense(FOCUSABLE)
-        }
-
-        /**
-         * Sense clicks and hover, but not drags.
-         */
-        fun click(): Sense {
-            return Sense(CLICK or FOCUSABLE)
-        }
-
-        /**
-         * Sense drags and hover, but not clicks.
-         */
-        fun drag(): Sense {
-            return Sense(DRAG or FOCUSABLE)
-        }
-
-        /**
-         * Sense both clicks, drags and hover (e.g. a slider or window).
-         *
-         * Note that this will introduce a latency when dragging,
-         * because when the user starts a press egui can't know if this is the start
-         * of a click or a drag, and it won't know until the cursor has
-         * either moved a certain distance, or the user has released the mouse button.
-         */
-        fun clickAndDrag(): Sense {
-            return Sense(CLICK or FOCUSABLE or DRAG)
-        }
-    }
-}
\ No newline at end of file
diff --git a/scripting/commonMain/kotlin/com/dropbear/ui/StrokeKind.kt b/scripting/commonMain/kotlin/com/dropbear/ui/StrokeKind.kt
deleted file mode 100644
index 38663be..0000000
--- a/scripting/commonMain/kotlin/com/dropbear/ui/StrokeKind.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.dropbear.ui
-
-/**
- * Describes how the stroke of a shape should be painted.
- */
-enum class StrokeKind {
-    /**
-     * The stroke should be painted entirely inside the shape
-     */
-    Inside,
-
-    /**
-     * The stroke should be painted right on the edge of the shape, half inside and half outside.
-     */
-    Middle,
-
-    /**
-     * The stroke should be painted entirely outside the shape
-     */
-    Outside,
-}
\ No newline at end of file
diff --git a/scripting/commonMain/kotlin/com/dropbear/ui/UIContainer.kt b/scripting/commonMain/kotlin/com/dropbear/ui/UIContainer.kt
new file mode 100644
index 0000000..78395e8
--- /dev/null
+++ b/scripting/commonMain/kotlin/com/dropbear/ui/UIContainer.kt
@@ -0,0 +1,33 @@
+package com.dropbear.ui
+
+import com.dropbear.ui.widgets.*
+
+interface UIContainer {
+    fun addChild(child: UIElement)
+    fun getChildren(): List<UIElement>
+
+    fun Circle(block: Circle.() -> Unit) {
+        val circle = Circle().apply(block)
+        addChild(circle)
+    }
+
+    fun Rectangle(block: Rectangle.() -> Unit) {
+        val rectangle = Rectangle().apply(block)
+        addChild(rectangle)
+    }
+
+    fun Image(block: Image.() -> Unit) {
+        val image = Image().apply(block)
+        addChild(image)
+    }
+
+    fun Text(block: Text.() -> Unit) {
+        val text = Text().apply(block)
+        addChild(text)
+    }
+
+    fun DynamicBoundingBox(block: DynamicBoundingBox.() -> Unit) {
+        val box = DynamicBoundingBox().apply(block)
+        addChild(box)
+    }
+}
\ No newline at end of file
diff --git a/scripting/commonMain/kotlin/com/dropbear/ui/UIElement.kt b/scripting/commonMain/kotlin/com/dropbear/ui/UIElement.kt
new file mode 100644
index 0000000..71cf2ec
--- /dev/null
+++ b/scripting/commonMain/kotlin/com/dropbear/ui/UIElement.kt
@@ -0,0 +1,12 @@
+package com.dropbear.ui
+
+import com.dropbear.DropbearEngine
+
+open class UIElement {
+    var name: String = "noNamedUIElement"
+    internal var parent: UIElement? = null
+
+    var onClick: ((DropbearEngine, UIElement) -> Unit)? = null
+    var onHover: ((DropbearEngine, UIElement) -> Unit)? = null
+    var onHoverExit: ((DropbearEngine, UIElement) -> Unit)? = null
+}
\ No newline at end of file
diff --git a/scripting/commonMain/kotlin/com/dropbear/ui/Ui.kt b/scripting/commonMain/kotlin/com/dropbear/ui/Ui.kt
index b75719a..2783f3d 100644
--- a/scripting/commonMain/kotlin/com/dropbear/ui/Ui.kt
+++ b/scripting/commonMain/kotlin/com/dropbear/ui/Ui.kt
@@ -11,18 +11,5 @@ import com.dropbear.utils.ID
  * All content will be rendered the next frame.
  */
 class Ui internal constructor() {
-    /**
-     * Draws/adds a [Widget] to the viewport. Typically used for HUD's and menus.
-     *
-     * The drawn widget will return a [Response], in which it is alive for 3 frames if
-     * not altered or rerendered.
-     */
-    fun add(widget: Widget): Response {
-        return widget.draw(this)
-    }
-}
 
-internal expect fun Ui.pushRect(rect: Rectangle)
-internal expect fun Ui.pushCircle(circle: Circle)
-
-internal expect fun Ui.wasClicked(id: ID): Boolean
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/scripting/commonMain/kotlin/com/dropbear/ui/Widget.kt b/scripting/commonMain/kotlin/com/dropbear/ui/Widget.kt
deleted file mode 100644
index c055024..0000000
--- a/scripting/commonMain/kotlin/com/dropbear/ui/Widget.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.dropbear.ui
-
-import com.dropbear.utils.ID
-
-/**
- * An abstract class all drawable objects must inherit
- */
-abstract class Widget(id: ID) {
-    /**
-     * Draws the object.
-     */
-    abstract fun draw(ui: Ui) : Response
-}
\ No newline at end of file
diff --git a/scripting/commonMain/kotlin/com/dropbear/ui/WidgetType.kt b/scripting/commonMain/kotlin/com/dropbear/ui/WidgetType.kt
deleted file mode 100644
index be1a6d3..0000000
--- a/scripting/commonMain/kotlin/com/dropbear/ui/WidgetType.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.dropbear.ui
-
-object WidgetType {
-    const val RECTANGLE = 0
-    const val CIRCLE = 1
-}
\ No newline at end of file
diff --git a/scripting/commonMain/kotlin/com/dropbear/ui/primitive/Circle.kt b/scripting/commonMain/kotlin/com/dropbear/ui/primitive/Circle.kt
deleted file mode 100644
index 2e71d7e..0000000
--- a/scripting/commonMain/kotlin/com/dropbear/ui/primitive/Circle.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.dropbear.ui.primitive
-
-import com.dropbear.math.Vector2d
-import com.dropbear.ui.Response
-import com.dropbear.ui.Ui
-import com.dropbear.ui.Widget
-import com.dropbear.ui.pushCircle
-import com.dropbear.utils.ID
-
-/**
- * Creates a standard circle.
- *
- * @property center The center of the circle.
- * @property radius The distance from the perimeter of the circle to the center/the radius.
- */
-class Circle(
-    val id: ID,
-    var center: Vector2d,
-    var radius: Double,
-): Widget(id) {
-    override fun draw(ui: Ui): Response {
-        ui.pushCircle(this)
-        return Response(id, ui)
-    }
-}
\ No newline at end of file
diff --git a/scripting/commonMain/kotlin/com/dropbear/ui/primitive/Rectangle.kt b/scripting/commonMain/kotlin/com/dropbear/ui/primitive/Rectangle.kt
deleted file mode 100644
index 146cb8e..0000000
--- a/scripting/commonMain/kotlin/com/dropbear/ui/primitive/Rectangle.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-package com.dropbear.ui.primitive
-
-import com.dropbear.math.Vector2d
-import com.dropbear.ui.Response
-import com.dropbear.ui.StrokeKind
-import com.dropbear.ui.Ui
-import com.dropbear.ui.Widget
-import com.dropbear.ui.pushRect
-import com.dropbear.utils.Colour
-import com.dropbear.utils.ID
-
-/**
- * @property initial The top left position/coordinate of the rectangle.
- * @property width The width of the rectangle
- * @property height The height of the rectangle
- * @property cornerRadius The radius of the corner. The higher the value, the more smoothened out the rect is
- * @property fillColour The colour of inside the rectangle. By default, it is [Colour.WHITE]
- * @property stroke The thickness/width of the stroke.
- * @property strokeColour The colour of the stroke. By default, it is [Colour.BLACK].
- * @property strokeKind Describes the stroke of a shape
- */
-class Rectangle(
-    val id: ID,
-    var initial: Vector2d,
-    var width: Double,
-    var height: Double,
-    var cornerRadius: Double = 0.0,
-    var fillColour: Colour = Colour.WHITE,
-    var stroke: Double = 0.0,
-    var strokeColour: Colour = Colour.BLACK,
-    var strokeKind: StrokeKind = StrokeKind.Middle,
-): Widget(id) {
-    override fun draw(ui: Ui): Response {
-        ui.pushRect(this)
-        return Response(id, ui)
-    }
-}
\ No newline at end of file
diff --git a/scripting/commonMain/kotlin/com/dropbear/ui/styling/Fill.kt b/scripting/commonMain/kotlin/com/dropbear/ui/styling/Fill.kt
new file mode 100644
index 0000000..d62ed6d
--- /dev/null
+++ b/scripting/commonMain/kotlin/com/dropbear/ui/styling/Fill.kt
@@ -0,0 +1,5 @@
+package com.dropbear.ui.styling
+
+import com.dropbear.utils.Colour
+
+class Fill(colour: Colour = Colour.TRANSPARENT)
\ No newline at end of file
diff --git a/scripting/commonMain/kotlin/com/dropbear/ui/styling/Stroke.kt b/scripting/commonMain/kotlin/com/dropbear/ui/styling/Stroke.kt
new file mode 100644
index 0000000..b7bfe75
--- /dev/null
+++ b/scripting/commonMain/kotlin/com/dropbear/ui/styling/Stroke.kt
@@ -0,0 +1,8 @@
+package com.dropbear.ui.styling
+
+import com.dropbear.utils.Colour
+
+class Stroke(
+    colour: Colour = Colour.BLACK,
+    width: Double = 1.0,
+)
\ No newline at end of file
diff --git a/scripting/commonMain/kotlin/com/dropbear/ui/styling/StyleConfig.kt b/scripting/commonMain/kotlin/com/dropbear/ui/styling/StyleConfig.kt
new file mode 100644
index 0000000..bcf168c
--- /dev/null
+++ b/scripting/commonMain/kotlin/com/dropbear/ui/styling/StyleConfig.kt
@@ -0,0 +1,6 @@
+package com.dropbear.ui.styling
+
+class StyleConfig {
+    var fill: Fill? = null
+    var stroke: Stroke? = null
+}
\ No newline at end of file
diff --git a/scripting/commonMain/kotlin/com/dropbear/ui/widgets/Circle.kt b/scripting/commonMain/kotlin/com/dropbear/ui/widgets/Circle.kt
new file mode 100644
index 0000000..21f24db
--- /dev/null
+++ b/scripting/commonMain/kotlin/com/dropbear/ui/widgets/Circle.kt
@@ -0,0 +1,22 @@
+package com.dropbear.ui.widgets
+
+import com.dropbear.ui.Align
+import com.dropbear.ui.UIContainer
+import com.dropbear.ui.UIElement
+
+class Circle : UIElement(), UIContainer {
+    var radius: Double = 0.0
+    var alignment: Align? = null
+    private val children = mutableListOf<UIElement>()
+
+    fun align(block: Align.() -> Unit) {
+        alignment = Align().apply(block)
+    }
+
+    override fun addChild(child: UIElement) {
+        child.parent = this
+        children.add(child)
+    }
+
+    override fun getChildren(): List<UIElement> = children.toList()
+}
\ No newline at end of file
diff --git a/scripting/commonMain/kotlin/com/dropbear/ui/widgets/DynamicBoundingBox.kt b/scripting/commonMain/kotlin/com/dropbear/ui/widgets/DynamicBoundingBox.kt
new file mode 100644
index 0000000..373913e
--- /dev/null
+++ b/scripting/commonMain/kotlin/com/dropbear/ui/widgets/DynamicBoundingBox.kt
@@ -0,0 +1,29 @@
+package com.dropbear.ui.widgets
+
+import com.dropbear.ui.UIContainer
+import com.dropbear.ui.UIElement
+
+class DynamicBoundingBox: UIElement(), UIContainer {
+    private val children = mutableListOf<UIElement>()
+
+    override fun addChild(child: UIElement) {
+        child.parent = this
+        children.add(child)
+    }
+
+    override fun getChildren(): List<UIElement> = children
+
+    fun findByName(name: String): UIElement? {
+        if (this.name == name) return this
+        for (child in children) {
+            if (child.name == name) return child
+            if (child is UIContainer) {
+                if (child is DynamicBoundingBox) {
+                    val found = child.findByName(name)
+                    if (found != null) return found
+                }
+            }
+        }
+        return null
+    }
+}
\ No newline at end of file
diff --git a/scripting/commonMain/kotlin/com/dropbear/ui/widgets/Image.kt b/scripting/commonMain/kotlin/com/dropbear/ui/widgets/Image.kt
new file mode 100644
index 0000000..0d227fb
--- /dev/null
+++ b/scripting/commonMain/kotlin/com/dropbear/ui/widgets/Image.kt
@@ -0,0 +1,9 @@
+package com.dropbear.ui.widgets
+
+import com.dropbear.ui.Align
+import com.dropbear.ui.UIElement
+
+class Image : UIElement() {
+    var resource: String = ""
+    var align: Align? = null
+}
\ No newline at end of file
diff --git a/scripting/commonMain/kotlin/com/dropbear/ui/widgets/Rectangle.kt b/scripting/commonMain/kotlin/com/dropbear/ui/widgets/Rectangle.kt
new file mode 100644
index 0000000..9bf05a8
--- /dev/null
+++ b/scripting/commonMain/kotlin/com/dropbear/ui/widgets/Rectangle.kt
@@ -0,0 +1,26 @@
+package com.dropbear.ui.widgets
+
+import com.dropbear.math.Vector2d
+import com.dropbear.ui.Align
+import com.dropbear.ui.UIContainer
+import com.dropbear.ui.UIElement
+import com.dropbear.ui.styling.StyleConfig
+
+class Rectangle : UIElement(), UIContainer {
+    var size: Vector2d = Vector2d(0.0, 0.0)
+    var align: Align? = null
+    var styleConfig: StyleConfig? = null
+
+    private val children = mutableListOf<UIElement>()
+
+    fun style(block: StyleConfig.() -> Unit) {
+        styleConfig = StyleConfig().apply(block)
+    }
+
+    override fun addChild(child: UIElement) {
+        child.parent = this
+        children.add(child)
+    }
+
+    override fun getChildren(): List<UIElement> = children.toList()
+}
\ No newline at end of file
diff --git a/scripting/commonMain/kotlin/com/dropbear/ui/widgets/Text.kt b/scripting/commonMain/kotlin/com/dropbear/ui/widgets/Text.kt
new file mode 100644
index 0000000..2f0a2c5
--- /dev/null
+++ b/scripting/commonMain/kotlin/com/dropbear/ui/widgets/Text.kt
@@ -0,0 +1,12 @@
+package com.dropbear.ui.widgets
+
+import com.dropbear.ui.Align
+import com.dropbear.ui.UIElement
+import com.dropbear.utils.Colour
+
+class Text : UIElement() {
+    var content: String = "empty..."
+    var align: Align? = null
+    var fontSize: Double = 12.0
+    var color: Colour = Colour.BLACK
+}
\ No newline at end of file
diff --git a/scripting/commonMain/kotlin/com/dropbear/utils/Colour.kt b/scripting/commonMain/kotlin/com/dropbear/utils/Colour.kt
index b0c6930..a7cd539 100644
--- a/scripting/commonMain/kotlin/com/dropbear/utils/Colour.kt
+++ b/scripting/commonMain/kotlin/com/dropbear/utils/Colour.kt
@@ -18,6 +18,7 @@ class Colour(
     var a: UByte,
 ) {
     companion object {
+        val TRANSPARENT = Colour(0u, 0u, 0u, 255u)
         val WHITE = Colour(0u, 0u, 0u, 255u)
         val BLACK = Colour(255u, 255u, 255u, 255u)
 
diff --git a/scripting/jvmMain/java/com/dropbear/ui/UINative.java b/scripting/jvmMain/java/com/dropbear/ui/UINative.java
deleted file mode 100644
index 1e920e4..0000000
--- a/scripting/jvmMain/java/com/dropbear/ui/UINative.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.dropbear.ui;
-
-import com.dropbear.EucalyptusCoreLoader;
-import com.dropbear.ui.primitive.Circle;
-import com.dropbear.ui.primitive.Rectangle;
-
-public class UINative {
-    static {
-        new EucalyptusCoreLoader().ensureLoaded();
-    }
-
-    public static native void pushRect(long uiBufferHandle, Rectangle rect);
-    public static native void pushCircle(long uiBufferHandle, Circle circle);
-    public static native boolean wasClicked(long uiBufferHandle, long id);
-}
diff --git a/scripting/jvmMain/kotlin/com/dropbear/ui/Ui.jvm.kt b/scripting/jvmMain/kotlin/com/dropbear/ui/Ui.jvm.kt
index fd58d92..3101b8e 100644
--- a/scripting/jvmMain/kotlin/com/dropbear/ui/Ui.jvm.kt
+++ b/scripting/jvmMain/kotlin/com/dropbear/ui/Ui.jvm.kt
@@ -1,24 +1 @@
-package com.dropbear.ui
-
-import com.dropbear.DropbearEngine
-import com.dropbear.ui.primitive.Circle
-import com.dropbear.ui.primitive.Rectangle
-import com.dropbear.utils.ID
-
-internal actual fun Ui.wasClicked(id: ID): Boolean {
-    return UINative.wasClicked(DropbearEngine.native.uiBufferHandle, id.getId())
-}
-
-internal actual fun Ui.pushRect(rect: Rectangle) {
-    UINative.pushRect(
-        DropbearEngine.native.uiBufferHandle,
-        rect
-    )
-}
-
-internal actual fun Ui.pushCircle(circle: Circle) {
-    UINative.pushCircle(
-        DropbearEngine.native.uiBufferHandle,
-        circle
-    )
-}
\ No newline at end of file
+package com.dropbear.ui
\ No newline at end of file
diff --git a/scripting/nativeMain/kotlin/com/dropbear/ui/Ui.native.kt b/scripting/nativeMain/kotlin/com/dropbear/ui/Ui.native.kt
index e730a47..766fce1 100644
--- a/scripting/nativeMain/kotlin/com/dropbear/ui/Ui.native.kt
+++ b/scripting/nativeMain/kotlin/com/dropbear/ui/Ui.native.kt
@@ -1,18 +1 @@
 package com.dropbear.ui
-
-import com.dropbear.DropbearEngine
-import com.dropbear.math.Vector2d
-import com.dropbear.ui.primitive.Circle
-import com.dropbear.ui.primitive.Rectangle
-import com.dropbear.utils.ID
-
-internal actual fun Ui.wasClicked(id: ID): Boolean {
-    TODO("Not yet implemented")
-}
-internal actual fun Ui.pushRect(rect: Rectangle) {
-    TODO("Not yet implemented")
-}
-
-internal actual fun Ui.pushCircle(circle: Circle) {
-    TODO("Not yet implemented")
-}
\ No newline at end of file