diff --git a/demo/demo/grid.cpp b/demo/demo/grid.cpp
index 16cec01e9a5032b3fa3f37d2dd7a4907a948edad..d3e659fdf07b7cf8d70e487237cf17d34550132e 100644
--- a/demo/demo/grid.cpp
+++ b/demo/demo/grid.cpp
@@ -74,7 +74,7 @@ namespace demo {
 		}
 	}
 
-	static void build_test_env(DemoGrid* area) {
+	static fggl::entity::EntityID build_test_env(DemoGrid* area) {
 		area->clear();
 		build_room(area, {5, 5}, {4,4});
 		build_room(area, {11, 5}, {1,1});
@@ -85,17 +85,63 @@ namespace demo {
 		build_room(area, {25, 5}, {3,3});
 
 		// player
+		fggl::entity::EntityID player = fggl::entity::INVALID;
 		auto& manager = area->entities();
 		{
-			auto player = manager.create();
+			player = manager.create();
 			auto& cellPos = manager.add<CellPos>(player);
 			cellPos.pos = {5,5};
 			cellPos.direction = 1;
 		}
+		return player;
 	}
 
+	struct Action {
+		const char* name;
+		std::function<void(void)> callback;
+	};
+
 	GridScene::GridScene(fggl::App &app) : GameBase(app), m_tiles(), m_animator(15.0F), m_grid(nullptr) {
 		m_animator.add([this](){this->tickPlayer();});
+
+		std::array<Action, 4> actions{{
+										  {"<", [=]() { this->rotate(true); }},
+										  {">", [=]() { this->rotate(false); }},
+										  {"^", [=]() { this->forward(); }},
+										  {"Z", [=]() {  } }
+									  }};
+
+		fggl::math::vec2i pos{0, 0};
+		for (auto& action : actions) {
+			fggl::math::vec2i size{32, 32};
+			auto btn = std::make_unique<fggl::gui::Button>(pos, size);
+			btn->label(action.name);
+			btn->addCallback([=](){
+				this->m_program.m_instructions.push_back(action.callback);
+			});
+			m_canvas.add(std::move(btn));
+
+			if ( pos.x == 0 ) {
+				pos.x += 32 + 8;
+			} else {
+				pos.x = 0;
+				pos.y += 32 + 8;
+			}
+		}
+
+		{
+			fggl::math::vec2i size{32, 32};
+			auto btn = std::make_unique<fggl::gui::Button>(pos, size);
+			btn->label("go");
+			btn->addCallback([=](){
+				if ( !this->m_program.playing ) {
+					this->m_program.m_currInstruction = 0;
+					this->m_program.playing = true;
+				}
+			});
+			m_canvas.add(std::move(btn));
+		}
+
 	}
 
 	void GridScene::activate() {
@@ -107,13 +153,11 @@ namespace demo {
 		// fake loading the tileset
 		if ( m_tiles.m_floors.empty() ) {
 			build_tileset(m_tiles);
-			//auto* assetLoader = m_owner.service<fggl::assets::Loader>();
-			//assetLoader->load("tileset_base.yml", ASSET_TILESET);
 		}
 
 		// create the grid world
 		m_grid = std::make_unique<DemoGrid>(m_tiles);
-		build_test_env(m_grid.get());
+		m_player = build_test_env(m_grid.get());
 	}
 
 	void GridScene::deactivate() {
@@ -177,72 +221,67 @@ namespace demo {
 		}
 	}
 
+	static void update_canvas(fggl::input::Input& inputs, fggl::gui::Container& canvas) {
+		fggl::math::vec2f cursorPos {
+			inputs.mouse.axis(fggl::input::MouseAxis::X),
+			inputs.mouse.axis(fggl::input::MouseAxis::Y)
+		};
+
+		// in canvas space
+		fggl::math::vec2 projected {
+			fggl::math::rescale_ndc(cursorPos.x, 0, 1920.f),
+			fggl::math::rescale_ndc(cursorPos.y, 0, 1080.0f)
+		};
+
+		auto *hoverWidget = canvas.getChildAt(projected);
+		/*if (hoverWidget != m_hover) {
+			if (m_hover != nullptr) {
+				m_hover->onExit();
+			}
+			m_hover = hoverWidget;
+			if (m_hover != nullptr) {
+				m_hover->onEnter();
+			}
+		}*/
+
+		if (inputs.mouse.pressed(fggl::input::MouseButton::LEFT)) {
+			auto* widget = canvas.getChildAt(projected);
+			if (widget != nullptr) {
+				fggl::debug::info("Button clicked");
+				widget->activate();
+			}
+		}
+	}
+
 	void GridScene::update(float deltaTime) {
 		GameBase::update(deltaTime);
 		m_animator.update(deltaTime);
+
+		update_canvas(input(), m_canvas);
 	}
 
 	void GridScene::tickPlayer() {
+		if ( !m_program.playing ){
+			return;
+		}
+
 		auto &manager = m_grid->entities();
-		auto entities = manager.find<CellPos>();
-		for (const auto &entity : entities) {
-			auto &pos = manager.get<CellPos>(entity);
-			pos.direction = (pos.direction + 1) % 4;
+		if ( m_program.m_currInstruction < m_program.m_instructions.size() ) {
+			m_program.m_instructions[ m_program.m_currInstruction ]();
+			m_program.m_currInstruction++;
+		} else {
+			m_program.playing = false;
+			m_program.m_currInstruction = 0;
+			m_program.m_instructions.clear();
 		}
 	}
 
-	//float progress = 0.0f;
 	void GridScene::render(fggl::gfx::Graphics &gfx) {
 		fggl::gfx::Paint paint;
 		render_grid(paint, *m_grid);
 		render_objects(paint, *m_grid);
 
-		/*
-		// draw test shapes to check grid alignment
-		for (int sides = 3; sides <= 25; ++sides) {
-			auto hexTest = fggl::gfx::make_shape(fggl::math::vec2{sides, 5} * DRAW_SIZE, DRAW_HALF, sides, {1.0F - (sides / 25.0F), 0.0f, sides / 25.0f});
-			paint.fill(hexTest);
-		}
-
-		// draw with edges
-		for (int sides = 3; sides <= 25; ++sides) {
-			auto hexTest = fggl::gfx::make_shape(fggl::math::vec2{sides, 6} * DRAW_SIZE, DRAW_HALF, sides, {1.0F - (sides / 25.0F), 0.0f, sides / 25.0f});
-			paint.stroke(hexTest);
-		}
-
-		// draw test arcs
-		for (int sides = 0; sides < 12; ++sides) {
-			float endAngle = progress * (M_PI * 2);
-
-			float startAngle = sides/12.0F * (M_PI * 2);
-			float endAngle2 = endAngle + startAngle;
-
-			auto hexTest = fggl::gfx::make_arc(fggl::math::vec2{sides + 3, 7} * DRAW_SIZE, DRAW_HALF, startAngle, endAngle2, fggl::gfx::colours::CYAN);
-			paint.fill(hexTest);
-		}
-
-		// draw sweep
-		for (int sides = 2; sides <= 25; ++sides){
-			float angle = progress * (M_PI * 2);
-			float sliceSize = 1 / (float)sides * (M_PI * 2);
-
-			float startAngle = angle;
-			float endAngle = startAngle + sliceSize;
-
-			auto hexTest = fggl::gfx::make_arc(fggl::math::vec2{sides + 3, 8} * DRAW_SIZE,
-											   DRAW_HALF,
-											   startAngle,
-											   endAngle,
-											   fggl::gfx::colours::CYAN);
-			paint.fill(hexTest);
-		}
-
-
-		progress += 0.01F;
-		if ( progress > 1.0F) {
-			progress = 0.0F;
-		}*/
-
+		m_canvas.render(paint);
 		gfx.draw2D(paint);
 	}
 
diff --git a/demo/include/grid.hpp b/demo/include/grid.hpp
index 380e7722a6a3da90e01143cde6fc030535168986..99d6b1160ef270d9a76bd1c94e2442835f1b97b9 100644
--- a/demo/include/grid.hpp
+++ b/demo/include/grid.hpp
@@ -22,9 +22,11 @@
 #include <memory>
 
 #include "fggl/scenes/game.hpp"
-#include "fggl/animation/animator.hpp"
 #include "fggl/entity/gridworld/zone.hpp"
 
+#include "fggl/animation/animator.hpp"
+#include "fggl/gui/gui.hpp"
+
 namespace demo {
 
 	constexpr int GRID_SIZE = 255;
@@ -42,6 +44,12 @@ namespace demo {
 		float rotationOffset{0.0F};
 	};
 
+	struct Program {
+		std::vector<std::function<void(void)>> m_instructions;
+		uint32_t m_currInstruction;
+		bool playing = false;
+	};
+
 	class GridScene : public fggl::scenes::GameBase {
 		public:
 			explicit GridScene(fggl::App& app);
@@ -54,9 +62,34 @@ namespace demo {
 			fggl::entity::grid::TileSet m_tiles;
 			fggl::animation::FrameAnimator m_animator;
 			std::unique_ptr<DemoGrid> m_grid;
+			fggl::gui::Container m_canvas;
+
+			fggl::entity::EntityID m_player = fggl::entity::INVALID;
+			Program m_program;
 
 			void tickPlayer();
 
+			inline void forward() {
+				auto& cell = m_grid->entities().get<CellPos>(m_player);
+				fggl::math::vec2i moveDir{0,0};
+				if ( cell.direction == 0) {
+					moveDir.y = 1;
+				} else if (cell.direction == 1) {
+					moveDir.x = 1;
+				} else if (cell.direction == 2) {
+					moveDir.y = -1;
+				} else if (cell.direction == 3) {
+					moveDir.x = -1;
+				}
+				cell.pos += moveDir;
+			}
+
+			inline void rotate(bool clockwise) {
+				auto& cell = m_grid->entities().get<CellPos>(m_player);
+				int direction = clockwise ? +1 : -1;
+				cell.direction = (cell.direction + 4 + direction) % 4;
+			}
+
 	};
 
 }
diff --git a/fggl/gui/widgets.cpp b/fggl/gui/widgets.cpp
index 12583aaa0b5247c4a8127ec1c03c4fce45661556..a35a8fa0c123f52589eeb5114070b9eea93c93f5 100644
--- a/fggl/gui/widgets.cpp
+++ b/fggl/gui/widgets.cpp
@@ -39,8 +39,8 @@ namespace fggl::gui {
 		if (m_active) {
 			for (auto &callback : m_callbacks) {
 				callback();
-				m_active = false;
 			}
+			m_active = false;
 		}
 	}
 
diff --git a/fggl/scenes/menu.cpp b/fggl/scenes/menu.cpp
index 2c38180e524c84fe6618d87cb55c23423170fd4e..1c0a64d6926f7a4e66d4ffb72786936574d65fac 100644
--- a/fggl/scenes/menu.cpp
+++ b/fggl/scenes/menu.cpp
@@ -92,7 +92,6 @@ namespace fggl::scenes {
 		// build the button
 		auto btn = std::make_unique<gui::Button>(pos, btnSize);
 		btn->label(name);
-
 		btn->addCallback(cb);
 		m_canvas.add(std::move(btn));
 	}