diff --git a/software/osu_main/src/gl/font.c b/software/osu_main/src/gl/font.c
new file mode 100644
index 0000000000000000000000000000000000000000..613aee2ff78722bece680b87bb8ffcedd056e6c3
--- /dev/null
+++ b/software/osu_main/src/gl/font.c
@@ -0,0 +1,61 @@
+#include "font.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "io.h"
+#include "gl.h"
+#include "regs.h"
+
+void gl_text(gl_text_ctx *context, const char *str, int x, int y) {
+	int idx;
+	for (char *c = str; *c; c++) {
+		idx = c - ' ';
+		if (idx < 0) {
+			// Render char 32 (unknown symbol)
+			context->x_[0][context->count_[0]] = x;
+			context->y_[0][context->count_[0]] = y;
+			context->count_[0]++;
+			x += context->font->widths[0];
+		} else if (idx == 0) {
+			// Skip
+			x += context->font->widths[0];
+		} else {
+			// Render char
+			context->x_[idx][context->count_[idx]] = x;
+			context->y_[idx][context->count_[idx]] = y;
+			context->count_[idx]++;
+			x += context->font->widths[idx];
+		}
+	}
+}
+
+void gl_text_flush(gl_text_ctx *context) {
+	gl_wait();
+	for (int c = 0; c < 96; c++) {
+		GL_IOWR(GL_ARGS, 0, context->font->font[i]);
+		GL_IOWR(GL_ARGS, 1, gl_make_point(context->font->widths[i], context->font->height));
+		int argptr = 2, cptr = context->count_[c];
+		while (cptr --> 0) {
+			GL_IOWR(GL_ARGS, argptr++, gl_make_point(context->x_[cptr], context->y_[cptr]));
+			if (argptr == 8) {
+				// Flush
+				GL_IOWR(GL_COMMAND, 0, GL_CMD_IMAGE);
+				gl_wait();
+				GL_IOWR(GL_COMMAND, 0, GL_CMD_NOOP);
+			}
+		}
+		if (argptr != 2) {
+			// Remaining undrawn characters. Flush
+			GL_IOWR(GL_ARGS, argptr, -1);
+			GL_IOWR(GL_COMMAND, 0, GL_CMD_IMAGE);
+			gl_wait();
+			GL_IOWR(GL_COMMAND, 0, GL_CMD_NOOP);
+		}
+	}
+}
+
+void gl_text_clear(gl_text_ctx *context) {
+	memset(context->count_, 0, sizeof(context->count_));
+}
diff --git a/software/osu_main/src/gl/font.h b/software/osu_main/src/gl/font.h
new file mode 100644
index 0000000000000000000000000000000000000000..b4b875b1a8fe16deecbb8df2a73eed43b986f443
--- /dev/null
+++ b/software/osu_main/src/gl/font.h
@@ -0,0 +1,17 @@
+#ifndef FONT_H_
+#define FONT_H_
+
+#include "../sdcard/osufs/osufs.h"
+#include <inttypes.h>
+
+typedef struct gl_text_ctx_t {
+	osu_font *font;
+	uint8_t count_[96];
+	uint16_t x_[96][128], y_[96][128];
+} gl_text_ctx;
+
+void gl_text(gl_text_ctx *context, const char *str, int x, int y);
+void gl_text_flush(gl_text_ctx *context);
+void gl_text_clear(gl_text_ctx *context);
+
+#endif