diff --git a/sysdata/libraries/horizon_api/src/lib.hb b/sysdata/libraries/horizon_api/src/lib.hb
index 1db3411..72f58d1 100644
--- a/sysdata/libraries/horizon_api/src/lib.hb
+++ b/sysdata/libraries/horizon_api/src/lib.hb
@@ -6,6 +6,7 @@ render := @use("../../../libraries/render/src/lib.hb")
 input := @use("../../intouch/src/lib.hb")
 
 widgets := @use("widgets.hb")
+ui := @use("ui.hb")
 
 WindowID := struct {
 	host_id: int,
diff --git a/sysdata/libraries/horizon_api/src/ui.hb b/sysdata/libraries/horizon_api/src/ui.hb
new file mode 100644
index 0000000..93fae2d
--- /dev/null
+++ b/sysdata/libraries/horizon_api/src/ui.hb
@@ -0,0 +1,50 @@
+stn := @use("../../../libraries/stn/src/lib.hb");
+.{string, log} := stn;
+.{Vec2} := stn.math
+
+render := @use("../../../libraries/render/src/lib.hb");
+.{Surface} := render;
+.{Font} := render.text
+
+UI := struct {
+	raw: ^u8,
+	raw_length: uint,
+	is_dirty: bool,
+	surface: Surface,
+}
+
+render_ui := fn(surface: Surface, ui: UI): void {
+	if ui.is_dirty {
+		render.clear(ui.surface, render.black)
+		ui.is_dirty = false
+	}
+	pos := Vec2(uint).(0, 0)
+	render.put_surface(surface, ui.surface, pos, false)
+}
+
+sexpr_parser := fn(sexpr: ^u8): UI {
+	cursor := sexpr
+	paren_balance := 0
+	loop {
+		if *cursor == 0 {
+			if paren_balance != 0 {
+				log.error("Unbalanced Parens\0")
+			}
+			break
+		} else if *cursor == 40 {
+			log.info("Open paren\0")
+			paren_balance += 1
+		} else if *cursor == 41 {
+			log.info("Closed paren\0")
+			paren_balance -= 1
+		}
+
+		cursor += 1
+	}
+
+	length := string.length(sexpr)
+
+	ui_surface := render.new_surface(100, 100)
+
+	return UI.(sexpr, length, true, ui_surface)
+}
\ No newline at end of file
diff --git a/sysdata/libraries/horizon_api/src/widgets.hb b/sysdata/libraries/horizon_api/src/widgets.hb
index 04574f2..9bf56f7 100644
--- a/sysdata/libraries/horizon_api/src/widgets.hb
+++ b/sysdata/libraries/horizon_api/src/widgets.hb
@@ -1,6 +1,13 @@
 // Widget types
 
 // End types
+stn := @use("../../../libraries/stn/src/lib.hb");
+.{string, log} := stn;
+.{Vec2} := stn.math
+
+render := @use("../../../libraries/render/src/lib.hb");
+.{Surface} := render;
+.{Font} := render.text
 
 LayoutChildHorizontalFirst := 0
 LayoutChildVerticalFirst := 1
@@ -12,9 +19,33 @@ Size := struct {
 	max_height: int,
 }
 
-Widget := struct {
-	size: Size,
-	clickable: bool,
-	layout: u8,
-	a: bool,
+Label := struct {
+	is_dirty: bool,
+	surface: Surface,
+	text: ^u8,
+	text_length: uint,
+}
+
+set_label_text := fn(label: Label, text: ^u8): void {
+	text_length := string.length(text)
+
+	label.is_dirty = true
+	label.text = text
+	label.text_length = text_length
+}
+
+render_label_to_surface := fn(surface: Surface, label: Label, font: Font, pos: Vec2(uint)): void {
+	if label.is_dirty {
+		render.clear(label.surface, render.black)
+		render.put_text(label.surface, font, .(0, 0), render.white, label.text)
+	}
+	render.put_surface(surface, label.surface, pos, false)
+	render.sync(surface)
+}
+
+new_label := fn(text: ^u8): Label {
+	text_surface := render.new_surface(100, 20)
+	text_length := string.length(text)
+	label := Label.(true, text_surface, text, text_length)
+	return label
 }
\ No newline at end of file
diff --git a/sysdata/programs/horizon/src/main.hb b/sysdata/programs/horizon/src/main.hb
index 5535bcd..c33882e 100644
--- a/sysdata/programs/horizon/src/main.hb
+++ b/sysdata/programs/horizon/src/main.hb
@@ -2,37 +2,15 @@ stn := @use("../../../libraries/stn/src/lib.hb");
 .{string, memory, buffer, random, log} := stn;
 .{Vec2} := stn.math
 
-horizon_api := @use("../../../libraries/horizon_api/src/lib.hb")
+horizon_api := @use("../../../libraries/horizon_api/src/lib.hb");
+.{new_label, render_label_to_surface, set_label_text} := horizon_api.widgets;
+.{sexpr_parser, render_ui} := horizon_api.ui
 
 render := @use("../../../libraries/render/src/lib.hb");
 .{Surface} := render;
 .{Font} := render.text
 intouch := @use("../../../libraries/intouch/src/lib.hb")
 
-Label := struct {is_dirty: bool, surface: Surface, text: ^u8, text_length: uint}
-set_label_text := fn(label: Label, text: ^u8, text_length: uint): void {
-	label.is_dirty = true
-	label.text = text
-	label.text_length = text_length
-}
-
-render_label_to_surface := fn(surface: Surface, label: Label, font: Font): void {
-	if label.is_dirty {
-		render.clear(label.surface, render.blue)
-		render.put_text(label.surface, font, .(0, 0), render.red, "hi\0")
-	}
-	pos := Vec2(uint).(100, 100)
-	render.put_surface(surface, label.surface, pos, false)
-	render.sync(surface)
-}
-
-new_label := fn(text: ^u8): Label {
-	text_surface := render.new_surface(100, 16)
-	text_length := string.length(text)
-	label := Label.(true, text_surface, text, text_length)
-	return label
-}
-
 Window := struct {
 	// TODO: Replace this with widgets
 	implicit_framebuffer: render.Surface,
@@ -64,6 +42,8 @@ main := fn(): int {
 	mouse_x := 0
 	mouse_y := 0
 	text_label := new_label("Hi\0")
+	widgets := "()\0"
+	ui := sexpr_parser(widgets)
 
 	loop {
 		// Clear the screen
@@ -87,6 +67,7 @@ main := fn(): int {
 			if mouse_event != null {
 				mouse_x += mouse_event.x_change
 				mouse_y += mouse_event.y_change
+				set_label_text(text_label, "Mouse Moved\0")
 			}
 			// render mouse
 			render.put_rect(screen, .(mouse_x, mouse_y), .(20, 20), render.white)
@@ -96,9 +77,10 @@ main := fn(): int {
 		// TODO: Get windows out of a collection and iter through
 
 		render.put_rect(screen, .(0, 0), .(screen.width - 1, screen.height - 1), render.white)
-
-		render_label_to_surface(screen, text_label, font)
-
+		{
+			pos := Vec2(uint).(1, screen.height - 21)
+			render_label_to_surface(screen, text_label, font, pos)
+		}
 		// Sync the screen
 		render.sync(screen)
 	}