diff --git a/demo/data/shader2D_frag.glsl b/demo/data/shader2D_frag.glsl
index 6d4fafa8f51abb0ef8ee365a8598e70f12bcb876..46f5a1e6519453616b5b31fd48da48a78c6a0618 100644
--- a/demo/data/shader2D_frag.glsl
+++ b/demo/data/shader2D_frag.glsl
@@ -1,9 +1,12 @@
 #version 330 core
+uniform sampler2D tex;
 
 in vec3 colour;
+in vec2 texPos;
+
 out vec4 FragColor;
 
 void main()
 {
-    FragColor = vec4(colour, 1.0f);
-} 
+    FragColor = vec4(colour.xyz, texture(tex, texPos).r);
+}
diff --git a/demo/data/shader2D_vert.glsl b/demo/data/shader2D_vert.glsl
index 223a72b07d65b8b5eae73a207bf661b0ebba5436..eff72d1e4f2710e72745c737b082f07e76cffebb 100644
--- a/demo/data/shader2D_vert.glsl
+++ b/demo/data/shader2D_vert.glsl
@@ -1,13 +1,16 @@
 #version 330 core
 layout (location = 0) in vec2 aPos;
 layout (location = 1) in vec3 aColour;
+layout (location = 2) in vec2 aTexPos;
 
 uniform mat4 projection;
 
 out vec3 colour;
+out vec2 texPos;
 
 void main()
 {
     gl_Position = projection * vec4(aPos, 0.0f, 1.0f);
     colour = aColour;
+    texPos = aTexPos;
 }
diff --git a/fggl/gfx/ogl/types.cpp b/fggl/gfx/ogl/types.cpp
index 7bb829337afaf915e818a209979988968632f96e..a6aba087a1262b6e9ddd8b5393769c4af23e1e48 100644
--- a/fggl/gfx/ogl/types.cpp
+++ b/fggl/gfx/ogl/types.cpp
@@ -131,5 +131,8 @@ namespace fggl::gfx::ogl {
 		#endif
 	}
 
+	void VertexArray::draw(Primative drawType, int first, std::size_t count) {
+		glDrawArrays( (GLenum)drawType, first, count);
+	}
 
 } // namespace fggl::gfx::ogl
\ No newline at end of file
diff --git a/fggl/gfx/ogl4/canvas.cpp b/fggl/gfx/ogl4/canvas.cpp
index 3ea8893691144096fbe5447fa4b6a268472a7402..729dbfedfa3fd31be0af0a528db0476d077b5c79 100644
--- a/fggl/gfx/ogl4/canvas.cpp
+++ b/fggl/gfx/ogl4/canvas.cpp
@@ -90,7 +90,7 @@ namespace fggl::gfx::ogl4 {
 		}
 	}
 
-	CanvasRenderer::CanvasRenderer() {
+	CanvasRenderer::CanvasRenderer() : m_fontTex(ogl::TextureType::Tex2D) {
 		m_vao.bind();
 
 		#ifdef FGGL_GL_I_BOUND
@@ -103,10 +103,12 @@ namespace fggl::gfx::ogl4 {
 		// define our attributes
 		auto posAttr = ogl::attribute<data::Vertex2D, math::vec2>(offsetof(data::Vertex2D, position));
 		auto colAttr = ogl::attribute<data::Vertex2D, math::vec3>(offsetof(data::Vertex2D, colour));
+		auto texAttr = ogl::attribute<data::Vertex2D, math::vec2>(offsetof(data::Vertex2D, texPos));
 
 		// bind the attributes to the vao
 		m_vao.setAttribute(m_vertexList, 0, posAttr);
 		m_vao.setAttribute(m_vertexList, 1, colAttr);
+		m_vao.setAttribute(m_vertexList, 2, texAttr);
 
 		#ifdef FGGL_GL_I_BOUND
 			// cool, rebind whatever happened before, or not
@@ -142,7 +144,85 @@ namespace fggl::gfx::ogl4 {
 		glUseProgram( 0 );
 	}
 
+	// slow version
 	void CanvasRenderer::renderText(const Paint& paint, GLuint shader) {
+		if ( paint.textCmds().empty() ){
+			return;
+		}
+
+		// get the expected font
+		auto fontFactory = util::ServiceLocator::instance().get<gui::FontLibrary>();
+		std::shared_ptr<gui::FontFace> face = fontFactory->getFont("LiberationSans-Regular.ttf");
+		if ( face == nullptr ){
+			// we don't know about that font...
+			return;
+		}
+
+		// setup the shader
+		glUseProgram( shader );
+		auto projMat = glm::ortho(0.0f, 1920.0f, 1080.0f, 0.f);
+		glUniformMatrix4fv(glGetUniformLocation( shader, "projection"), 1, GL_FALSE,
+						   glm::value_ptr(projMat));
+
+		// bind the vbo we'll use for writing
+		m_vao.bind();
+
+		glEnable(GL_BLEND);
+		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+		// for each text string, attempt to render it
+		for ( const auto& textCmd : paint.textCmds() ) {
+			const auto label = textCmd.text;
+			math::vec2 penPos(textCmd.pos);
+
+			for ( auto letter : label ) {
+				ogl::Image img{};
+				img.format = ogl::ImageFormat::R;
+				img.type = ogl::PixelFormat::UNSIGNED_BYTE;
+
+				glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+				face->texture(letter, img.size.x, img.size.y, &img.data);
+				m_fontTex.setData(ogl::InternalImageFormat::Red, img);
+
+				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+				m_fontTex.bind(1);
+				glUniform1i( glGetUniformLocation(shader, "tex"), 1);
+
+				// this is why this is called the slow version, we render each quad as a single call
+				auto& metrics = face->metrics(letter);
+				float xPos = penPos.x + metrics.bearing.x;
+				float yPos = (penPos.y - metrics.bearing.y);
+				float w = metrics.size.x;
+				float h = metrics.size.y;
+
+				const math::vec3 texCol{ 1.0f, 1.0f, 1.0f};
+
+				std::array<data::Vertex2D, 6> verts{{
+														{{xPos, yPos + h}, texCol, {0.0F, 1.0F}},
+														{{xPos, yPos}, texCol, {0.0F, 0.0F}},
+														{{xPos + w, yPos}, texCol, {1.0F, 0.0F}},
+														{{xPos, yPos + h}, texCol, {0.0F, 1.0F}},
+														{{xPos + w, yPos}, texCol, {1.0F, 0.0F}},
+														{{xPos + w, yPos + h}, texCol, {1.0F, 1.0F}},
+													}};
+				m_vertexList.replace(verts.size(), verts.data());
+
+				m_vao.bind();
+				m_vao.draw(ogl::Primative::TRIANGLE, 0, verts.size());
+
+				penPos.x += (metrics.advance >> 6 );
+			}
+		}
+
+		glDisable(GL_BLEND);
+	}
+
+	/*
+	void renderTextWIP(const Paint& paint, GLuint shader) {
 
 		if ( paint.textCmds().empty() ) {
 			return;
@@ -190,7 +270,7 @@ namespace fggl::gfx::ogl4 {
 
 		m_vao.drawElements(m_indexList, ogl::Primative::TRIANGLE, mesh.indexList.size());
 		glUseProgram( 0 );
-	}
+	}*/
 
 	void CanvasRenderer::render(GLuint shader, const gfx::Paint &paint) {
 		renderShapes(paint, shader);
diff --git a/fggl/gui/fonts.cpp b/fggl/gui/fonts.cpp
index 36a72bea3d6f488af4ded3c25913b8f673580fde..49bba26b7687e627dd5f5f998233c0f59c7dd823 100644
--- a/fggl/gui/fonts.cpp
+++ b/fggl/gui/fonts.cpp
@@ -59,4 +59,15 @@ namespace fggl::gui {
 		auto it = m_metrics.emplace(letter, metrics);
 		return it.first->second;
 	}
+
+	void FontFace::texture(char letter, int &width, int &height, void **buff) {
+		if (FT_Load_Char(m_face, letter, FT_LOAD_RENDER) ) {
+			// something bad happened
+			return;
+		}
+
+		width = m_face->glyph->bitmap.width;
+		height = m_face->glyph->bitmap.rows;
+		*buff = m_face->glyph->bitmap.buffer;
+	}
 } // namespace fggl::fonts
\ No newline at end of file
diff --git a/fggl/gui/widgets.cpp b/fggl/gui/widgets.cpp
index 77d5c459dff780042d9494d7089e0d9c54b1b3ab..467d2a11847b8cd4c7a9ace12d8008f5f1cabc03 100644
--- a/fggl/gui/widgets.cpp
+++ b/fggl/gui/widgets.cpp
@@ -9,14 +9,14 @@
 
 namespace fggl::gui {
 
-	Button::Button( math::vec2 pos, math::vec2 size) : Widget(pos, size), m_hover(false), m_active(false) {}
+	Button::Button( math::vec2 pos, math::vec2 size) : Widget(pos, size), m_label(pos, size), m_hover(false), m_active(false) {}
 
 	void Button::render(gfx::Paint &paint) {
 		gfx::Path2D path{ topLeft() };
 		draw_button(path, topLeft(), size(), m_hover, m_active);
 		paint.fill(path);
 
-		paint.text(m_value, topLeft());
+		m_label.render(paint);
 	}
 
 	void Button::activate() {
@@ -41,10 +41,29 @@ namespace fggl::gui {
 	}
 
 	void Button::label(const std::string &value) {
-		m_value = value;
+		m_label.text(value);
 	}
 
 	std::string Button::label() const {
-		return m_value;
+		return m_label.text();
+	}
+
+	void Label::layout() {
+		if ( m_font == nullptr ) {
+			return;
+		}
+
+		math::vec2 size;
+		for (const auto& letter : m_value) {
+			auto metrics = m_font->metrics(letter);
+			size.x += (metrics.advance << 6);
+			size.y = std::max(size.y, metrics.size.y);
+		}
+		m_naturalSize = size;
+		m_needsLayout = false;
+	}
+
+	Label::Label(math::vec2 pos, math::vec2 size) : Widget(pos, size) {
+
 	}
 } // namespace fggl::gui
\ No newline at end of file
diff --git a/include/fggl/data/model.hpp b/include/fggl/data/model.hpp
index 6dfa2cf11fad34f4aaed285da12e37e97a12e3a3..37be1766495f9abf889aa4df77b459aa1068e4dd 100644
--- a/include/fggl/data/model.hpp
+++ b/include/fggl/data/model.hpp
@@ -18,6 +18,7 @@ namespace fggl::data {
 	struct Vertex2D {
 		fggl::math::vec2 position;
 		fggl::math::vec3 colour;
+		fggl::math::vec2 texPos;
 	};
 
 	struct Mesh2D {
diff --git a/include/fggl/gfx/ogl/types.hpp b/include/fggl/gfx/ogl/types.hpp
index f84e46573100e530e9cd0b78cb6a1b9fd99767ef..b121b449953b263c8404d468ce85b81ba350361d 100644
--- a/include/fggl/gfx/ogl/types.hpp
+++ b/include/fggl/gfx/ogl/types.hpp
@@ -121,6 +121,157 @@ namespace fggl::gfx::ogl {
 			}
 	};
 
+	enum class Wrapping {
+		REPEAT = GL_REPEAT,
+		MIRRORED_REPEAT = GL_MIRRORED_REPEAT,
+		CLAMP_TO_EDGE = GL_CLAMP_TO_EDGE,
+		CLAMP_TO_BORDER = GL_CLAMP_TO_BORDER
+	};
+
+	enum class Filtering {
+		NEIGHBOUR = GL_NEAREST,
+		LINEAR = GL_LINEAR,
+
+		// these are their openGL names, but they are kinda bad, can we do better/alias?
+		NEAREST_MIPMAP_NEAREST = GL_NEAREST_MIPMAP_NEAREST,
+		LINEAR_MIPMAP_NEAREST = GL_LINEAR_MIPMAP_NEAREST,
+		NEAREST_MIPMAP_LINEAR = GL_NEAREST_MIPMAP_LINEAR,
+		LINEAR_MIPMAP_LINEAR = GL_LINEAR_MIPMAP_LINEAR,
+
+		MIPMAP_CLOSEST_NEIGHBOUR = GL_NEAREST_MIPMAP_NEAREST,
+		MIPMAP_CLOSEST_LINEAR = GL_LINEAR_MIPMAP_NEAREST,
+		TWO_MIPMAP_NEIGHBOUR = GL_NEAREST_MIPMAP_LINEAR,
+		TWO_MIPMAP_LINEAR = GL_LINEAR_MIPMAP_LINEAR
+	};
+
+	enum class TextureType {
+		Tex1D = GL_TEXTURE_1D,
+		Tex1DArray = GL_TEXTURE_1D_ARRAY,
+
+		Tex2D = GL_TEXTURE_2D,
+		Tex2DArray = GL_TEXTURE_2D_ARRAY,
+		Tex2DMultisample = GL_TEXTURE_2D_MULTISAMPLE,
+		Tex2DMultisampleArray = GL_TEXTURE_2D_MULTISAMPLE_ARRAY,
+		Tex3D = GL_TEXTURE_3D,
+
+		Rectangle = GL_TEXTURE_RECTANGLE,
+		CubeMap = GL_TEXTURE_CUBE_MAP,
+		CubeMapArray = GL_TEXTURE_CUBE_MAP_ARRAY,
+		Buffer = GL_TEXTURE_BUFFER,
+
+	};
+
+	enum class PixelFormat {
+			UNSIGNED_BYTE = GL_UNSIGNED_BYTE,
+			BYTE,
+			UNSIGNED_SHORT,
+			SHORT,
+			UNSIGNED_INT,
+			INT,
+			HALF_FLOAT,
+			FLOAT,
+
+			UNSIGNED_BYTE_3_3_2,
+			UNSIGNED_BYTE_2_3_3_REV,
+			UNSIGNED_SHORT_5_6_5,
+			UNSIGNED_SHORT_5_6_5_REV,
+			UNSINGED_SHORT_4_4_4_4,
+			UNSIGNED_SHORT_4_4_4_4_REV,
+			UNSIGNED_SHORT_5_5_5_1,
+			UNSIGNED_SHORT_1_5_5_5_REV,
+			UNSIGNED_INT_8_8_8_8,
+			UNSIGNED_INT_8_8_8_8_REV,
+			UNSIGNED_INT_10_10_10_10_2,
+			UNSIGNED_INT_10_10_10_10_2_REV,
+	};
+
+	enum class InternalImageFormat {
+			DepthComponent = GL_DEPTH_COMPONENT,
+			DepthStencil = GL_DEPTH_STENCIL,
+			Red = GL_RED,
+			RedGreen = GL_RG,
+			RedGreenBlue = GL_RGB,
+			RedGreenBlueAlpha = GL_RGBA
+	};
+
+	enum class ImageFormat {
+			R = GL_RED,
+			RG,
+			RGB,
+			RGBA,
+			R_INT,
+			RG_INT,
+			RGB_INT,
+			RGBA_INT,
+			BGR,
+			BGRA,
+			BGR_INT,
+			BGRA_INT,
+			STENTICL_INDEX,
+			DEPTH_COMPONENT,
+			DEPTH_STENCIL
+	};
+
+	struct Image {
+		PixelFormat type;
+		ImageFormat format;
+		math::vec2i size;
+		void* data;
+	};
+
+	class Texture {
+		public:
+			inline explicit Texture(TextureType type) : m_type(type) {
+				glGenTextures(1, &m_obj);
+			}
+			~Texture() {
+				glDeleteTextures(1, &m_obj);
+			}
+
+			void setup(InternalImageFormat iFmt, math::vec2i size) {
+				//bind();
+				glBindTexture( (GLenum)m_type, m_obj );
+				if ( iFmt == InternalImageFormat::DepthComponent ) {
+					glTexImage2D((GLenum)
+					m_type, 0, (GLint) iFmt, size.x, size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, nullptr);
+				} else {
+					glTexImage2D((GLenum)
+					m_type, 0, (GLint) iFmt, size.x, size.y, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
+				}
+			}
+
+			void setData(InternalImageFormat iFmt, Image& image) {
+				//bind();
+				glBindTexture( (GLenum)m_type, m_obj );
+				glTexImage2D( (GLenum)m_type, 0, (GLint)iFmt, image.size.x, image.size.y, 0, (GLenum)image.format, (GLenum)image.type, image.data);
+			}
+
+			void setDataPart(math::vec2i offset, Image& image) {
+				glTexImage2D( (GLenum)m_type, 0, offset.x, offset.y, image.size.x, image.size.y, (GLenum)image.format, (GLenum)image.type, image.data);
+			}
+
+			void wrapMode(Wrapping wrap);
+			void filterModeMagnify(Filtering filter);
+			void filterModeMinify(Filtering filter);
+
+			/**
+			 * Bind this texture to a texture unit.
+			 *
+			 * @param textureUnit the texture unit to bind to
+			 */
+			inline void bind(unsigned int textureUnit) {
+				assert( 0 <= textureUnit && textureUnit < GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS );
+				glActiveTexture( GL_TEXTURE0 + textureUnit );
+				glBindTexture( (GLenum) m_type,  m_obj );
+			}
+
+		private:
+			GLuint m_obj = 0;
+			TextureType m_type;
+
+			void release();
+	};
+
 	enum class BuffAttrF {
 		HALF_FLOAT = GL_HALF_FLOAT,
 		FLOAT = GL_FLOAT,
@@ -143,30 +294,30 @@ namespace fggl::gfx::ogl {
 	};
 
 	enum class Primative {
-			POINT = GL_POINT,
-			LINE = GL_LINES,
-			LINE_STRIP = GL_LINE_STRIP,
-			LINE_LOOP = GL_LINE_LOOP,
-			TRIANGLE = GL_TRIANGLES,
-			TRIANGLE_STRIP = GL_TRIANGLE_STRIP,
-			TRIANGLE_FAN = GL_TRIANGLE_FAN
+		POINT = GL_POINT,
+		LINE = GL_LINES,
+		LINE_STRIP = GL_LINE_STRIP,
+		LINE_LOOP = GL_LINE_LOOP,
+		TRIANGLE = GL_TRIANGLES,
+		TRIANGLE_STRIP = GL_TRIANGLE_STRIP,
+		TRIANGLE_FAN = GL_TRIANGLE_FAN
 	};
 
 	enum class BufType {
-			ARRAY = GL_ARRAY_BUFFER,
-			ELEMENT_ARRAY = GL_ELEMENT_ARRAY_BUFFER,
-			COPY_READ = GL_COPY_READ_BUFFER,
-			COPY_WRITE = GL_COPY_WRITE_BUFFER,
-			PIXEL_UNPACK = GL_PIXEL_UNPACK_BUFFER,
-			PIXEL_PACK = GL_PIXEL_PACK_BUFFER,
-			QUERY = GL_QUERY_BUFFER,
-			TEXTURE = GL_TEXTURE_BUFFER,
-			TRANSFORM_FEEDBACK = GL_TRANSFORM_FEEDBACK_BUFFER,
-			UNIFORM = GL_UNIFORM_BUFFER,
-			DRAW_INDRECT = GL_DRAW_INDIRECT_BUFFER,
-			ATOMIC_COUNTER = GL_ATOMIC_COUNTER_BUFFER,
-			DISPATCH_INDIRECT = GL_DISPATCH_INDIRECT_BUFFER,
-			SHADER_STORAGE = GL_SHADER_STORAGE_BUFFER
+		ARRAY = GL_ARRAY_BUFFER,
+		ELEMENT_ARRAY = GL_ELEMENT_ARRAY_BUFFER,
+		COPY_READ = GL_COPY_READ_BUFFER,
+		COPY_WRITE = GL_COPY_WRITE_BUFFER,
+		PIXEL_UNPACK = GL_PIXEL_UNPACK_BUFFER,
+		PIXEL_PACK = GL_PIXEL_PACK_BUFFER,
+		QUERY = GL_QUERY_BUFFER,
+		TEXTURE = GL_TEXTURE_BUFFER,
+		TRANSFORM_FEEDBACK = GL_TRANSFORM_FEEDBACK_BUFFER,
+		UNIFORM = GL_UNIFORM_BUFFER,
+		DRAW_INDRECT = GL_DRAW_INDIRECT_BUFFER,
+		ATOMIC_COUNTER = GL_ATOMIC_COUNTER_BUFFER,
+		DISPATCH_INDIRECT = GL_DISPATCH_INDIRECT_BUFFER,
+		SHADER_STORAGE = GL_SHADER_STORAGE_BUFFER
 	};
 
 	enum class BufUsage {
@@ -288,7 +439,7 @@ namespace fggl::gfx::ogl {
 	template<typename V, typename T>
 	AttributeF attribute(std::size_t offset) {
 		return AttributeF{
-			 attr_type<T>::attr,
+			attr_type<T>::attr,
 			attr_type<T>::size,
 			sizeof(V),
 			offset
@@ -328,6 +479,7 @@ namespace fggl::gfx::ogl {
 			void setAttributeI(const ArrayBuffer& buffer, GLuint idx, AttributeI& attr);
 
 			void drawElements(const ElementBuffer& buff, Primative drawType, std::size_t size);
+			void draw(Primative drawType, int first, std::size_t count);
 	};
 
 	// paranoid functions
@@ -337,7 +489,7 @@ namespace fggl::gfx::ogl {
 	template<BufType T>
 	void bind_buffer(GLuint* marker, const Buffer<T>& buff) {
 		#ifdef FGGL_GL_PARANOID
-			assert( marker != nullptr );
+		assert( marker != nullptr );
 			glGetIntegerv( (GLenum)T, (GLint*) marker );
 		#endif
 		buff.bind();
@@ -346,7 +498,7 @@ namespace fggl::gfx::ogl {
 	template<BufType T>
 	void unbind_buffer(GLuint* marker, const Buffer<T>& buff) {
 		#ifdef GL_FGGL_PARANOID
-			assert( marker != nullptr );
+		assert( marker != nullptr );
 			glBindVertexArray(marker);
 		#endif
 	}
diff --git a/include/fggl/gfx/ogl4/canvas.hpp b/include/fggl/gfx/ogl4/canvas.hpp
index 57cf6a5e805f92c243073fa1e1da6ea59da625b4..6c90bff32cf512d8a33983bdbfe3a495c1527ac1 100644
--- a/include/fggl/gfx/ogl4/canvas.hpp
+++ b/include/fggl/gfx/ogl4/canvas.hpp
@@ -21,7 +21,9 @@ namespace fggl::gfx::ogl4 {
 			ogl::VertexArray m_vao;
 			ogl::ArrayBuffer m_vertexList;
 			ogl::ElementBuffer m_indexList;
+
 			gui::FontLibrary m_fonts;
+			ogl::Texture m_fontTex;
 
 			void renderShapes(const Paint &paint, GLuint shader);
 			void renderText(const Paint&, GLuint shader);
diff --git a/include/fggl/gui/fonts.hpp b/include/fggl/gui/fonts.hpp
index 735e07d68e0e3b43ba5d6abdd3208055a89a9474..3ff8f418cfe638b9a862ff1999116ca384b37320 100644
--- a/include/fggl/gui/fonts.hpp
+++ b/include/fggl/gui/fonts.hpp
@@ -46,9 +46,14 @@ namespace fggl::gui {
 
 	class FontFace {
 		public:
-			FontFace(FT_Face face);
+			explicit FontFace(FT_Face face);
 			~FontFace();
 
+			FontFace(const FontFace&) = delete;
+			FontFace(FontFace&&) = delete;
+			FontFace& operator=(const FontFace&) = delete;
+			FontFace& operator=(FontFace&&) = delete;
+
 			inline GlyphMetrics& metrics(char letter){
 				auto itr = m_metrics.find(letter);
 				if ( itr == m_metrics.end() ) {
@@ -58,7 +63,7 @@ namespace fggl::gui {
 			}
 
 			math::vec2 stringSize(const std::string& text);
-			void texture(char letter, int& width, int& height, unsigned char* buff);
+			void texture(char letter, int& width, int& height, void** buff);
 
 		private:
 			FT_Face m_face;
@@ -72,6 +77,12 @@ namespace fggl::gui {
 			FontLibrary();
 			~FontLibrary();
 
+			// copy and moving not needed
+			FontLibrary(const FontLibrary&) = delete;
+			FontLibrary(FontLibrary&&) = delete;
+			FontLibrary& operator=(const FontLibrary&) = delete;
+			FontLibrary& operator=(FontLibrary&&) = delete;
+
 			inline std::shared_ptr<FontFace> getFont(const std::string& name) {
 				auto fontItr = m_cache.find(name);
 				if ( fontItr != m_cache.end() ) {
@@ -86,7 +97,7 @@ namespace fggl::gui {
 				if ( FT_New_Face(m_context, path.c_str(), 0, &face) ) {
 					return nullptr;
 				}
-				FT_Set_Pixel_Sizes(face, 0, 48);
+				FT_Set_Pixel_Sizes(face, 0, 18);
 
 				// create the new font object
 				auto ptr = std::make_shared<FontFace>(face);
diff --git a/include/fggl/gui/widgets.hpp b/include/fggl/gui/widgets.hpp
index 8f7ccaa59d314341315666be3044f3858f3e5b9f..4749b2023c23a6198f60d3b40947b408e905a499 100644
--- a/include/fggl/gui/widgets.hpp
+++ b/include/fggl/gui/widgets.hpp
@@ -2,12 +2,59 @@
 #define FGGL_GUI_WIDGETS_H
 
 #include <functional>
-#include <fggl/gui/widget.hpp>
+#include <utility>
+
+#include "fggl/gui/widget.hpp"
+#include "fggl/gui/fonts.hpp"
 
 namespace fggl::gui {
 
 	using Callback = std::function<void(void)>;
 
+	class Label : public Widget {
+		public:
+			Label() = default;
+			Label(math::vec2 pos, math::vec2 size);
+
+			~Label() = default;
+
+			void render(gfx::Paint &paint) override {
+				auto pos = topLeft();
+				auto size2 = size();
+				math::vec2 baseLine{pos.x + 10, pos.y + size2.y/2 + 5};
+				paint.text(m_value,baseLine);
+			}
+
+			inline void font(std::shared_ptr<FontFace> font) {
+				m_font = std::move(font);
+				m_needsLayout = true;
+			}
+
+			inline void text(const std::string& value) {
+				m_value = value;
+				m_needsLayout = true;
+			}
+
+			inline std::string text() const {
+				return m_value;
+			}
+
+			math::vec2 naturalSize() {
+				if ( m_needsLayout ) {
+					layout();
+				}
+				return m_naturalSize;
+			}
+
+			void layout();
+
+		private:
+			std::shared_ptr<gui::FontFace> m_font;
+			std::string m_value;
+			bool m_needsLayout;
+			math::vec2 m_naturalSize;
+	};
+
 	class Button : public Widget {
 		public:
 			Button() = default;
@@ -26,28 +73,17 @@ namespace fggl::gui {
 
 			void addCallback(Callback callback);
 		private:
+			Label m_label;
 			std::string m_value;
 			std::vector<Callback> m_callbacks;
 			bool m_hover;
 			bool m_active;
 	};
 
-	class Label : public Widget {
-		public:
-			Label();
-			void render(gfx::Paint &paint) override;
-	};
-
-	class TextField : public Widget {
-		public:
-			TextField();
-			void render(gfx::Paint &paint) override;
-	};
 
-	class Toggle : public Widget {
+	class Toggle : public Button {
 		public:
-			Toggle();
-			void render(gfx::Paint &paint) override;
+			Toggle() = default;
 	};
 
 }; //namespace fggl::gui