diff --git a/include/ui.h b/include/ui.h
index e184fd9..82a629d 100644
--- a/include/ui.h
+++ b/include/ui.h
@@ -37,6 +37,8 @@ struct ui_ctx {
   i64 active;
   i64 mouse_x;
   i64 mouse_y;
+  i64 rel_x;
+  i64 rel_y;
   bool mouse_down;
   bool mouse_up;
   const window_t *wnd;
@@ -47,5 +49,6 @@ void reset_ui_ctx(ui_ctx_t *ctx);
 void handle_ui_events(const window_t *wnd, ui_ctx_t *ctx,
                       const SDL_Event *event);
 bool button(const window_t *wnd, ui_ctx_t *ctx, rect_t rect);
+rect_t node(const window_t *wnd, ui_ctx_t *ctx, rect_t rect);
 
 #endif // !UI_H
diff --git a/src/compositor.c b/src/compositor.c
index ac9104f..1c1f7c1 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -27,6 +27,7 @@ struct compositor {
   u64 last_clicked_mouse_y;
   i64 node_hovered;
   u64 count;
+  rect_t *rects;
   node_t *nodes;
   bool move_node;
   ui_ctx_t ctx;
@@ -41,6 +42,7 @@ i32 run_main_loop(void) {
   }
 
   compositor_t comp = {0};
+  comp.rects = (rect_t *)malloc(sizeof(rect_t) * MAX_NODES);
   comp.nodes = (node_t *)malloc(sizeof(node_t) * MAX_NODES);
 
   window_t *main_window = &(comp.windows[0]);
@@ -63,6 +65,13 @@ i32 run_main_loop(void) {
 
   colour_t bg_colour = {.abgr = 0xffffffff};
 
+  comp.rects[0] = (rect_t){
+      .topleft.x = 150,
+      .topleft.y = 50,
+      .w = NODE_WIDTH,
+      .h = NODE_HEIGHT,
+  };
+
   while (comp.running) {
     while (SDL_PollEvent(&(comp.event))) {
       for (u64 i = 0; i < MAX_WINDOWS; ++i) {
@@ -125,6 +134,8 @@ i32 run_main_loop(void) {
       printf("Button 2 pressed\n");
     }
 
+    comp.rects[0] = node(main_window, &(comp.ctx), comp.rects[0]);
+
     for (u64 i = 0; i < MAX_WINDOWS; ++i) {
       swap_buffers(&(comp.windows[i]));
     }
diff --git a/src/ui.c b/src/ui.c
index b915d31..aa21aca 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -32,12 +32,17 @@ void reset_ui_ctx(ui_ctx_t *ctx) {
   ctx->count = 0;
   ctx->mouse_down = false;
   ctx->mouse_up = false;
+  ctx->rel_x = 0;
+  ctx->rel_y = 0;
 }
 
 void handle_ui_events(const window_t *wnd, ui_ctx_t *ctx,
                       const SDL_Event *event) {
   switch (event->type) {
   case SDL_MOUSEMOTION:
+    ctx->rel_x = event->motion.xrel;
+    ctx->rel_y = event->motion.yrel;
+
     if (wnd->id == event->motion.windowID) {
       ctx->mouse_x = event->motion.x;
       ctx->mouse_y = event->motion.y;
@@ -45,7 +50,6 @@ void handle_ui_events(const window_t *wnd, ui_ctx_t *ctx,
 
       break;
     }
-
   case SDL_MOUSEBUTTONDOWN:
     if (wnd->id == event->button.windowID) {
       ctx->mouse_x = event->button.x;
@@ -102,3 +106,47 @@ bool button(const window_t *wnd, ui_ctx_t *ctx, rect_t rect) {
 
   return false;
 }
+
+rect_t node(const window_t *wnd, ui_ctx_t *ctx, rect_t rect) {
+  if (ctx->count + 1 >= MAX_UI_ELEMENTS) {
+    return (rect_t){0};
+  }
+
+  ui_elem_t elem = (ui_elem_t){
+      .id = (ctx->count)++,
+      .rect = rect,
+      .type = UI_ELEM_BUTTON,
+  };
+
+  fill_rect(wnd, rect, colours[UI_ELEM_BUTTON].fill);
+  draw_rect(wnd, rect, colours[UI_ELEM_BUTTON].border);
+
+  if (wnd != ctx->wnd) {
+    return rect;
+  }
+
+  if (ctx->mouse_up) {
+    ctx->hovered = ctx->active = -1;
+  }
+
+  if (ctx->hovered == elem.id && ctx->active == elem.id) {
+    return (rect_t){
+        .topleft.x = rect.topleft.x + ctx->rel_x,
+        .topleft.y = rect.topleft.y + ctx->rel_y,
+        .w = rect.w,
+        .h = rect.h,
+    };
+  }
+
+  if (!aabb(&elem, ctx->mouse_x, ctx->mouse_y)) {
+    return rect;
+  }
+
+  ctx->hovered = elem.id;
+
+  if (ctx->mouse_down) {
+    ctx->active = elem.id;
+  }
+
+  return rect;
+}