diff --git a/sysdata/libraries/render/src/lib.hb b/sysdata/libraries/render/src/lib.hb
index c72b8cd..00279a3 100644
--- a/sysdata/libraries/render/src/lib.hb
+++ b/sysdata/libraries/render/src/lib.hb
@@ -38,14 +38,20 @@ $light_cyan := Color.(255, 255, 0, 255)
 put_pixel := mode.put_pixel
 put_rect := mode.put_rect
 put_filled_rect := mode.put_filled_rect
+put_trirect := mode.put_trirect
+put_circle := mode.put_circle
+put_filled_circle := mode.put_filled_circle
+put_textured_circle := mode.put_textured_circle
 put_line := mode.put_line
+put_vline := mode.put_vline
+put_hline := mode.put_hline
 clear := mode.clear
 put_surface := mode.put_surface
 put_text := mode.put_text
 // thanks peony for these three!
-put_trirect := mode.put_trirect
-put_vline := mode.put_vline
-put_hline := mode.put_hline
+//put_trirect := mode.put_trirect
+//put_vline := mode.put_vline
+//put_hline := mode.put_hline
 
 // Display
 sync := mode.sync
\ No newline at end of file
diff --git a/sysdata/libraries/render/src/software.hb b/sysdata/libraries/render/src/software.hb
index efc8d92..0c4780e 100644
--- a/sysdata/libraries/render/src/software.hb
+++ b/sysdata/libraries/render/src/software.hb
@@ -251,6 +251,90 @@ put_hline := fn(surface: Surface, y: uint, x0: uint, x1: uint, color: Color): vo
 
 	return
 }
+
+put_circle := fn(surface: Surface, pos: Vec2(uint), radius: uint, color: Color): void {
+	x := 0
+	y := radius
+	error := @as(int, 3) - @as(int, @intcast(2 * radius));
+	*@inline(indexptr, surface, pos.x + radius, pos.y) = color;
+	*@inline(indexptr, surface, pos.x - radius, pos.y) = color;
+	*@inline(indexptr, surface, pos.x, pos.y + radius) = color;
+	*@inline(indexptr, surface, pos.x, pos.y - radius) = color
+
+	loop if y < x break else {
+		x += 1
+
+		if error > 0 {
+			y -= 1
+			error += 4 * (@as(int, @intcast(x)) - @as(int, @intcast(y))) + 10
+		} else {
+			error += 4 * @intcast(x) + 6
+		};
+		*@inline(indexptr, surface, pos.x + x, pos.y + y) = color;
+		*@inline(indexptr, surface, pos.x + y, pos.y + x) = color;
+		*@inline(indexptr, surface, pos.x - x, pos.y + y) = color;
+		*@inline(indexptr, surface, pos.x - y, pos.y + x) = color;
+		*@inline(indexptr, surface, pos.x + x, pos.y - y) = color;
+		*@inline(indexptr, surface, pos.x + y, pos.y - x) = color;
+		*@inline(indexptr, surface, pos.x - x, pos.y - y) = color;
+		*@inline(indexptr, surface, pos.x - y, pos.y - x) = color
+	}
+
+	return
+}
+
+put_filled_circle := fn(surface: Surface, pos: Vec2(uint), radius: uint, color: Color): void {
+	x := 0
+	y := radius
+	error := @as(int, 3) - @as(int, @intcast(2 * radius))
+	@inline(put_hline, surface, pos.y - x, pos.x - radius, pos.x + radius, color);
+	*@inline(indexptr, surface, pos.x, pos.y + radius) = color;
+	*@inline(indexptr, surface, pos.x, pos.y - radius) = color
+
+	loop if y < x break else {
+		x += 1
+
+		if error > 0 {
+			@inline(put_hline, surface, pos.y + y, pos.x - x, pos.x + x, color)
+			@inline(put_hline, surface, pos.y - y, pos.x - x, pos.x + x, color)
+			y -= 1
+			error += 4 * (@as(int, @intcast(x)) - @as(int, @intcast(y))) + 10
+		} else {
+			error += 4 * @intcast(x) + 6
+		}
+		@inline(put_hline, surface, pos.y + x, pos.x - y, pos.x + y, color)
+		@inline(put_hline, surface, pos.y - x, pos.x - y, pos.x + y, color)
+	}
+
+	return
+}
+
+put_textured_circle := fn(surface: Surface, source: Surface, source_pos: Vec2(uint), pos: Vec2(uint), radius: uint): void {
+	x := 0
+	y := radius
+	error := @as(int, 3) - @as(int, @intcast(2 * radius))
+	@inline(memory.copy, Color, @inline(indexptr, source, source_pos.x - y, source_pos.y), @inline(indexptr, surface, pos.x - y, pos.y), 2 * y);
+	*@inline(indexptr, surface, pos.x, pos.y + y) = *@inline(indexptr, source, source_pos.x, source_pos.y + y);
+	*@inline(indexptr, surface, pos.x, pos.y - y) = *@inline(indexptr, source, source_pos.x, source_pos.y - y)
+
+	loop if y < x break else {
+		x += 1
+
+		if error > 0 {
+			@inline(memory.copy, Color, @inline(indexptr, source, source_pos.x - x, source_pos.y + y), @inline(indexptr, surface, pos.x - x, pos.y + y), 2 * x)
+			@inline(memory.copy, Color, @inline(indexptr, source, source_pos.x - x, source_pos.y - y), @inline(indexptr, surface, pos.x - x, pos.y - y), 2 * x)
+			y -= 1
+			error += 4 * (@as(int, @intcast(x)) - @as(int, @intcast(y))) + 10
+		} else {
+			error += 4 * @intcast(x) + 6
+		}
+		@inline(memory.copy, Color, @inline(indexptr, source, source_pos.x - y, source_pos.y + x), @inline(indexptr, surface, pos.x - y, pos.y + x), 2 * y)
+		@inline(memory.copy, Color, @inline(indexptr, source, source_pos.x - y, source_pos.y - x), @inline(indexptr, surface, pos.x - y, pos.y - x), 2 * y)
+	}
+
+	return
+}
+
 utf8_len_table := [u8].(0, 0, 2, 3)
 
 put_text := fn(surface: Surface, font: Font, pos: Vec2(uint), color: Color, str: ^u8): void {
diff --git a/sysdata/programs/render_example/src/examples/orbit.hb b/sysdata/programs/render_example/src/examples/orbit.hb
new file mode 100644
index 0000000..84b47e3
--- /dev/null
+++ b/sysdata/programs/render_example/src/examples/orbit.hb
@@ -0,0 +1,36 @@
+.{Vec2, sin, cos, PI} := @use("../../../../libraries/stn/src/lib.hb").math
+render := @use("../../../../libraries/render/src/lib.hb")
+
+able_bmp := @embed("../../../../assets/able.bmp")
+mini_bmp := @embed("../../../../assets/mini.bmp")
+
+/* expected result:
+   two textured circles rotating
+   around one yellow filled circle
+   with a blue line showing their
+   'orbit' */
+
+example := fn(): void {
+	able := render.image.from(@bitcast(&able_bmp))
+	mini := render.image.from(@bitcast(&mini_bmp))
+	if able == null | mini == null {
+		return
+	}
+
+	angle := 0.0
+
+	screen := render.init(true)
+
+	loop {
+		render.clear(screen, render.black)
+		render.put_filled_circle(screen, .(screen.width / 2, screen.height / 2), 128, render.light_yellow)
+		render.put_circle(screen, .(screen.width / 2, screen.height / 2), 256, render.light_blue)
+		// Precision issues?
+		render.put_textured_circle(screen, able, .(able.width / 2, able.height / 2), .(screen.width / 2 + @intcast(@fti(sin(angle) * 256)), screen.height / 2 + @intcast(@fti(cos(angle) * 256))), able.width / 2 - 1)
+		render.put_textured_circle(screen, mini, .(mini.width / 2, mini.height / 2), .(screen.width / 2 + @intcast(@fti(sin(angle + PI) * 256)), screen.height / 2 + @intcast(@fti(cos(angle + PI) * 256))), mini.width / 2 - 1)
+		render.sync(screen)
+
+		angle += 0.01
+	}
+	return
+}
\ No newline at end of file
diff --git a/sysdata/programs/render_example/src/main.hb b/sysdata/programs/render_example/src/main.hb
index 77cb04c..ab66cfa 100644
--- a/sysdata/programs/render_example/src/main.hb
+++ b/sysdata/programs/render_example/src/main.hb
@@ -1 +1 @@
-.{example: main} := @use("./examples/text.hb")
\ No newline at end of file
+.{example: main} := @use("./examples/orbit.hb")
\ No newline at end of file
diff --git a/sysdata/system_config.toml b/sysdata/system_config.toml
index 1992a80..93bd739 100644
--- a/sysdata/system_config.toml
+++ b/sysdata/system_config.toml
@@ -22,14 +22,14 @@ resolution = "1024x768x24"
 
 [boot.limine.ableos.modules]
 
-# [boot.limine.ableos.modules.render_example]
-# path = "boot:///render_example.hbf"
+[boot.limine.ableos.modules.render_example]
+path = "boot:///render_example.hbf"
 
-[boot.limine.ableos.modules.horizon]
-path = "boot:///horizon.hbf"
+# [boot.limine.ableos.modules.horizon]
+# path = "boot:///horizon.hbf"
 
-[boot.limine.ableos.modules.ps2_mouse_driver]
-path = "boot:///ps2_mouse_driver.hbf"
+# [boot.limine.ableos.modules.ps2_mouse_driver]
+# path = "boot:///ps2_mouse_driver.hbf"
 
 # [boot.limine.ableos.modules.ps2_keyboard_driver]
 # path = "boot:///ps2_keyboard_driver.hbf"