diff --git a/odin/sdl-opengl-rendering/src/main.odin b/odin/sdl-opengl-rendering/src/main.odin
index 9eb33fb..b4a7fa6 100644
--- a/odin/sdl-opengl-rendering/src/main.odin
+++ b/odin/sdl-opengl-rendering/src/main.odin
@@ -9,13 +9,15 @@ import "vendor:OpenGL"
 import "vendor:sdl3"
 
 State :: struct {
-	tick:      u32,
-	window:    ^sdl3.Window,
-	program:   u32,
-	rect_vao:  VAO,
-	wireframe: bool,
+	tick:       u32,
+	window:     ^sdl3.Window,
+	program:    u32,
+	rect_vao:   VAO,
+	circle_vao: VAO,
+	wireframe:  bool,
 }
 
+// {{{ Initialization
 init :: proc() -> (state: State, ok: bool) {
 	GL_MAJOR :: 3
 	GL_MINOR :: 3
@@ -93,6 +95,8 @@ init :: proc() -> (state: State, ok: bool) {
 	OpenGL.load_up_to(GL_MAJOR, GL_MINOR, sdl3.gl_set_proc_address)
 	OpenGL.ClearColor(0, 0, 0, 1)
 	OpenGL.Enable(OpenGL.DEPTH_TEST)
+	OpenGL.Enable(OpenGL.BLEND)
+	OpenGL.BlendFunc(OpenGL.SRC_ALPHA, OpenGL.ONE_MINUS_SRC_ALPHA)
 
 	state.program = OpenGL.load_shaders_source(
 		#load("./vert.glsl"),
@@ -101,11 +105,35 @@ init :: proc() -> (state: State, ok: bool) {
 
 	state.rect_vao = create_vao({{-1, -1}, {1, -1}, {1, 1}, {-1, 1}}, {0, 1, 2, 3}) or_return
 
+	CIRCLE_POINTS :: 127
+	circle_vertices: [CIRCLE_POINTS + 1]ℝ²
+	circle_indices: [CIRCLE_POINTS + 2]u32
+
+	for i in 0 ..< CIRCLE_POINTS {
+		θ := ℝ(i) * 2 * math.π / CIRCLE_POINTS
+		circle_vertices[i + 1] = {math.cos(θ), math.sin(θ)}
+		circle_indices[i + 1] = u32(i + 1)
+	}
+
+	circle_indices[CIRCLE_POINTS + 1] = circle_indices[1]
+
+	state.circle_vao = create_vao(circle_vertices[:], circle_indices[:]) or_return
+
 	init_command_queue()
 
 	return state, true
 }
+// }}}
+// {{{ Close
+close :: proc(state: State) {
+	OpenGL.DeleteProgram(state.program)
+	_ = sdl3.StopTextInput(state.window)
+	sdl3.DestroyWindow(state.window)
+	sdl3.Quit()
+}
+// }}}
 
+// {{{ Render
 render :: proc(state: ^State) {
 	state.tick += 1
 	OpenGL.Clear(OpenGL.COLOR_BUFFER_BIT | OpenGL.DEPTH_BUFFER_BIT)
@@ -131,20 +159,22 @@ render :: proc(state: ^State) {
 					2 /
 					ℝ(count) -
 				1
-			draw_rect(Rect{pos, 1 / ℝ(count), 0, {(pos.x + 1) / 2, (pos.y + 1) / 2, 1, 1}})
+			color := Color{(pos.x + 1) / 2, (pos.y + 1) / 2, 1, 1}
+
+			if x > y {
+				draw_rect(pos, 1 / ℝ(count), color)
+			} else {
+				r := 1 / ℝ(count) / 2
+				draw_circle(pos + r, r, color)
+			}
 		}
 	}
 
+	draw_circle({-0.25, -0.3}, 0.6, Color{0, 0, 0.5, 0.75}, z = -0.1)
 	render_queue(state)
 }
-
-close :: proc(state: State) {
-	OpenGL.DeleteProgram(state.program)
-	_ = sdl3.StopTextInput(state.window)
-	sdl3.DestroyWindow(state.window)
-	sdl3.Quit()
-}
-
+// }}}
+// {{{ Main
 main :: proc() {
 	log.Level_Headers = {
 		0 ..< 10 = "[DEBUG] ",
@@ -186,3 +216,4 @@ main :: proc() {
 		free_all(context.temp_allocator)
 	}
 }
+// }}}
diff --git a/odin/sdl-opengl-rendering/src/shape.odin b/odin/sdl-opengl-rendering/src/shape.odin
index 2ccc6d3..34932e7 100644
--- a/odin/sdl-opengl-rendering/src/shape.odin
+++ b/odin/sdl-opengl-rendering/src/shape.odin
@@ -12,25 +12,25 @@ Mat3 :: matrix[3, 3]ℝ
 Color :: [4]ℝ
 // }}}
 // {{{ Command queues
+Shape :: struct {
+	z:    ℝ,
+	fill: Color,
+}
 Rect :: struct {
-	top_left:   ℝ²,
-	dimensions: ℝ²,
-	z:          ℝ,
-	fill:       Color,
+	using shape: Shape,
+	top_left:    ℝ²,
+	dimensions:  ℝ²,
 }
 
 Circle :: struct {
-	center: ℝ²,
-	radius: ℝ,
-}
-
-Shape :: union {
-	Rect,
-	Circle,
+	using shape: Shape,
+	center:      ℝ²,
+	radius:      ℝ,
 }
 
 Command_Queue :: struct {
-	rects: [dynamic]Rect,
+	rects:   [dynamic]Rect,
+	circles: [dynamic]Circle,
 }
 
 queue: ^Command_Queue
@@ -43,7 +43,9 @@ init_command_queue :: proc() {
 }
 
 draw_rect_args :: proc(top_left: ℝ², dimensions: ℝ², color: Color, z: ℝ = 0) {
-	draw_rect_struct(Rect{top_left, dimensions, z, color})
+	draw_rect_struct(
+		Rect{top_left = top_left, dimensions = dimensions, shape = Shape{z = z, fill = color}},
+	)
 }
 
 draw_rect_struct :: proc(rect: Rect) {
@@ -51,10 +53,24 @@ draw_rect_struct :: proc(rect: Rect) {
 }
 
 draw_rect :: proc {
-	draw_rect_args,
 	draw_rect_struct,
+	draw_rect_args,
 }
 
+draw_circle_args :: proc(center: ℝ², radius: ℝ, color: Color, z: ℝ = 0) {
+	draw_circle_struct(
+		Circle{center = center, radius = radius, shape = Shape{z = z, fill = color}},
+	)
+}
+
+draw_circle_struct :: proc(circle: Circle) {
+	append(&queue.circles, circle)
+}
+
+draw_circle :: proc {
+	draw_circle_struct,
+	draw_circle_args,
+}
 // }}}
 
 // {{{ VAO & consts
@@ -167,6 +183,36 @@ set_rect_transforms :: proc(vao: ^VAO, rects: []Rect) -> ℕ {
 
 	return len(rects)
 }
+
+set_circle_transforms :: proc(vao: ^VAO, circles: []Circle) -> ℕ {
+	log.assert(len(circles) <= INSTANCES, "Attempting to send too many circles to the GPU")
+	matrices := new([INSTANCES]Mat3, context.temp_allocator)
+	fills := new([INSTANCES]Color, context.temp_allocator)
+
+	for circle, i in circles {
+		mat: Mat3
+
+		mat[0, 0] = circle.radius
+		mat[1, 1] = circle.radius
+		mat[2].xy = circle.center.xy
+		mat[2].z = circle.z
+
+		matrices[i] = mat
+		fills[i] = circle.fill
+	}
+
+	OpenGL.BindBuffer(OpenGL.ARRAY_BUFFER, vao.instance_mat_buffer)
+	OpenGL.BufferData(
+		OpenGL.ARRAY_BUFFER,
+		INSTANCES * size_of(Mat3),
+		matrices,
+		OpenGL.DYNAMIC_DRAW,
+	)
+	OpenGL.BindBuffer(OpenGL.ARRAY_BUFFER, vao.instance_fill_buffer)
+	OpenGL.BufferData(OpenGL.ARRAY_BUFFER, INSTANCES * size_of(Color), fills, OpenGL.DYNAMIC_DRAW)
+
+	return len(circles)
+}
 // }}}
 // {{{ Render the entire queue
 draw_instances :: proc(vao: VAO, instances: ℕ) {
@@ -191,5 +237,15 @@ render_queue :: proc(state: ^State) {
 	}
 
 	clear(&queue.rects)
+	circle_steps := len(queue.circles) / INSTANCES
+
+	for i := 0; i < len(queue.circles); i += INSTANCES {
+		slice := queue.circles[i:]
+		if len(slice) > INSTANCES {slice = slice[:INSTANCES]}
+		instances := set_circle_transforms(&state.circle_vao, slice)
+		draw_instances(state.circle_vao, instances)
+	}
+
+	clear(&queue.circles)
 }
 // }}}