diff --git a/.gitignore b/.gitignore
index 6b9ebd9..6d45ee2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,3 @@
-libgearlib.a
-obj/
-test/*.exe
+*.a
+obj/
+examples/*.exe
diff --git a/Makefile b/Makefile
index 48fcc5e..2929a13 100644
--- a/Makefile
+++ b/Makefile
@@ -1,25 +1,25 @@
-LIBRARY=libgearlib.a
-
-CC:=gcc
-AR=ar
-CFLAGS=-O3 -Iinclude -g
-LDFLAGS=-lglfw -lm
-
-CFILES=$(shell cd src && mingw32-find -L * -type f -name '*.c')
-OBJDIR=obj
-OBJ=$(addprefix $(OBJDIR)/, $(CFILES:.c=.o))
-
-$(LIBRARY): $(OBJ) Makefile
-	$(AR) rcs $(LIBRARY) $(OBJ)
-
-obj/%.o: src/%.c
-	mkdir -p $(OBJDIR)
-	$(CC) $(CFLAGS) -c $< -o $@
-
-run: $(LIBRARY)
-	make -C examples test
-	cd examples && ./test
-
-clean:
-	rm -rf $(OBJ) $(LIBRARY)
-	make -C test clean
+LIBRARY=libgearlib.a
+
+CC:=gcc
+AR=ar
+CFLAGS=-O3 -Iinclude -g
+LDFLAGS=-lglfw -lm
+
+CFILES=$(shell cd src && find -L * -type f -name '*.c')
+OBJDIR=obj
+OBJ=$(addprefix $(OBJDIR)/, $(CFILES:.c=.o))
+
+$(LIBRARY): $(OBJ) Makefile
+	$(AR) rcs $(LIBRARY) $(OBJ)
+
+obj/%.o: src/%.c
+	mkdir -p $(OBJDIR)
+	$(CC) $(CFLAGS) -c $< -o $@
+
+run: $(LIBRARY)
+	make -C examples test
+	cd examples && ./test
+
+clean:
+	rm -rf $(OBJ) $(LIBRARY)
+	make -C examples clean
diff --git a/examples/Makefile b/examples/Makefile
index 4d91e1e..876b0b9 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -1,16 +1,16 @@
-CC=gcc
-
-CFLAGS=-O3 -I../include
-LDFLAGS=C:\msys64\ucrt64\lib\libglfw3.a -L ../ -lgdi32 -lgearlib -lm
-
-CFILES=$(shell mingw32-find -L * -type f -name '*.c')
-BINARIES=$(CFILES:.c=)
-
-all: $(BINARIES)
-
-%: %.c
-	$(CC) $< -o $@ $(CFLAGS) $(LDFLAGS)
-
-clean:
-	rm -rf $(BINARIES)
-
+CC=gcc
+
+CFLAGS = -O3 -I../include
+LDFLAGS = -L ../ -lglfw -lgearlib -lm
+
+CFILES=$(shell find -L * -type f -name '*.c')
+BINARIES=$(CFILES:.c=)
+
+all: $(BINARIES)
+
+%: %.c
+	$(CC) $< -o $@ $(CFLAGS) $(LDFLAGS)
+
+clean:
+	rm -rf $(BINARIES)
+
diff --git a/examples/test b/examples/test
deleted file mode 100755
index df1c972..0000000
Binary files a/examples/test and /dev/null differ
diff --git a/examples/test.c b/examples/test.c
index 272eeba..d0c779a 100644
--- a/examples/test.c
+++ b/examples/test.c
@@ -1,35 +1,44 @@
-#include <gearlib.h>
-
-int main() {
-    Window window = create_window(800, 600, "test");
-    glfwSwapInterval(0); // fps unlock
-    enable_debugging();
-    setup_textures();
-
-    Camera* camera = create_camera(vec2(0.0f, 0.0f));
-    UniformBuffer* camera_buffer = create_uniform_buffer(sizeof(CameraMatrices));
-
-    Texture cat = load_texture("assets/cat.png");
-
-    while (!glfwWindowShouldClose(window)) {
-        process_input(window);
-        update_camera(camera, window);
-        set_uniform_data(camera_buffer, camera->m);
-
-        glClearColor(vec4_spread(BLACK));
-        glClear(GL_COLOR_BUFFER_BIT);
-
-        for(int i = 0; i < 32; i++) {
-            draw_texture(cat, vec2(rand() % 600, rand() % 600), vec2(500.0f, 500.0f), WHITE);
-        }
-
-        flush();
-    
-        glfwSwapBuffers(window);
-        glfwPollEvents();
-    }
-
-    glfwDestroyWindow(window);
-    glfwTerminate();
-    return 0;
-}
+#include <gearlib.h>
+
+int main() {
+    Window window = create_window(800, 600, "test");
+    glfwSwapInterval(0); // fps unlock
+    enable_debugging();
+    setup_textures();
+
+    Camera* camera = create_camera(vec2(0.0f, 0.0f));
+    UniformBuffer* camera_buffer = create_uniform_buffer(sizeof(CameraMatrices));
+
+    Texture cat = load_texture("assets/cat.png");
+
+    double time = glfwGetTime();
+
+    while (!glfwWindowShouldClose(window)) {
+        process_input(window);
+        update_camera(camera, window);
+        set_uniform_data(camera_buffer, camera->m);
+
+        glClearColor(vec4_spread(BLACK));
+        glClear(GL_COLOR_BUFFER_BIT);
+
+        for(int i = 0; i < 32; i++) {
+            draw_texture(cat, vec2(rand() % 600, rand() % 600), vec2(500.0f, 500.0f), WHITE);
+        }
+
+        flush();
+
+	double end_time = glfwGetTime();
+	double frame_time = end_time - time;
+	time = end_time;
+
+	double fps = 1.0 / frame_time;
+	printf("frame_time: %lf fps: %lf\n", frame_time, fps);
+    
+        glfwSwapBuffers(window);
+        glfwPollEvents();
+    }
+
+    glfwDestroyWindow(window);
+    glfwTerminate();
+    return 0;
+}
diff --git a/examples/test.exe b/examples/test.exe
index 35a279f..c85d807 100644
Binary files a/examples/test.exe and b/examples/test.exe differ
diff --git a/examples/textures b/examples/textures
deleted file mode 100755
index 12f5133..0000000
Binary files a/examples/textures and /dev/null differ
diff --git a/examples/textures.c b/examples/textures.c
index e8b75a0..0c7809b 100644
--- a/examples/textures.c
+++ b/examples/textures.c
@@ -1,32 +1,41 @@
-#include <gearlib.h>
-
-int main() {
-    Window window = create_window(800, 600, "textures");
-    glfwSwapInterval(0); // fps unlock
-    setup_textures();
-
-    Camera* camera = create_camera(vec2(0.0f, 0.0f));
-    UniformBuffer* camera_buffer = create_uniform_buffer(sizeof(CameraMatrices));
-
-    Texture cat = load_texture("assets/cat.png"); 
-
-    while (!glfwWindowShouldClose(window)) {
-        process_input(window);
-        update_camera(camera, window);
-        set_uniform_data(camera_buffer, camera->m);
-
-        glClearColor(vec4_spread(BLACK));
-        glClear(GL_COLOR_BUFFER_BIT);
-
-        draw_texture(cat, vec2(250.0f, 250.0f), vec2(500.0f, 500.0f), WHITE);
-
-        flush();
-    
-        glfwSwapBuffers(window);
-        glfwPollEvents();
-    }
-
-    glfwDestroyWindow(window);
-    glfwTerminate();
-    return 0;
-}
+#include <gearlib.h>
+
+int main() {
+    Window window = create_window(800, 600, "textures");
+    glfwSwapInterval(0); // fps unlock
+    setup_textures();
+
+    Camera* camera = create_camera(vec2(0.0f, 0.0f));
+    UniformBuffer* camera_buffer = create_uniform_buffer(sizeof(CameraMatrices));
+
+    Texture cat = load_texture("assets/cat.png");
+
+    double time = glfwGetTime();
+
+    while (!glfwWindowShouldClose(window)) {
+        process_input(window);
+        update_camera(camera, window);
+        set_uniform_data(camera_buffer, camera->m);
+
+        glClearColor(vec4_spread(BLUE));
+        glClear(GL_COLOR_BUFFER_BIT);
+
+        draw_texture(cat, vec2(250.0f, 250.0f), vec2(500.0f, 500.0f), WHITE);
+
+        flush();
+
+	double end_time = glfwGetTime();
+	double frame_time = end_time - time;
+	time = end_time;
+
+	double fps = 1.0 / frame_time;
+	printf("frame_time: %lf fps: %lf\n", frame_time, fps);
+    
+        glfwSwapBuffers(window);
+        glfwPollEvents();
+    }
+
+    glfwDestroyWindow(window);
+    glfwTerminate();
+    return 0;
+}
diff --git a/src/.textures.c.swp b/src/.textures.c.swp
new file mode 100644
index 0000000..a34c85d
Binary files /dev/null and b/src/.textures.c.swp differ
diff --git a/src/textures.c b/src/textures.c
index 215ddce..1192f21 100644
--- a/src/textures.c
+++ b/src/textures.c
@@ -1,119 +1,129 @@
-#include <textures.h>
-#include <stb_image.h>
-#include <opengl.h>
-#include <stdio.h>
-#include <assert.h>
-
-RenderBatch* texture_quad_batch = NULL;
-int max_textures;
-
-void setup_textures() {
-    texture_quad_batch = create_texture_quad_batch();
-
-    glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_textures);
-    if(max_textures > 32) max_textures = 32;
-}
-
-uint32_t load_texture(const char* path) {
-    uint32_t id;
-    glCreateTextures(GL_TEXTURE_2D, 1, &id);
-
-    glTextureParameteri(id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-    glTextureParameteri(id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-    glTextureParameteri(id, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-    glTextureParameteri(id, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
-    int width, height, channels;
-    unsigned char* pixels = stbi_load(path, &width, &height, &channels, 4);
-    if(pixels) {
-        glTextureStorage2D(id, 1, GL_RGBA8, width, height);
-        glTextureSubImage2D(id, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
-        printf("[Texture %d, %s] Loaded successfully (%dx%d, %d channels)\n", id, path, width, height, channels);
-    } else {
-        printf("[Texture %d, %s] Failed to load image data: %s\n", id, path, stbi_failure_reason());
-        exit(-1);
-    }
-
-    return id;
-}
-
-vec2 texture_quad_texcoords[] = {
-    { 0.0f, 1.0f },
-    { 0.0f, 0.0f },
-    { 1.0f, 0.0f },
-        
-    { 0.0f, 1.0f },
-    { 1.0f, 0.0f },
-    { 1.0f, 1.0f }
-};
-
-void draw_texture(Texture id, vec2 pos, vec2 size, vec4 tint) { 
-    mat4 transform = mat4Multiply(
-            mat4Scale(size.x, size.y, 1.0f),
-            mat4Translate(pos.x, pos.y, 0.0f));
-
-    draw_texture_trans(id, transform, tint);
-}
-
-void draw_texture_trans(Texture id, mat4 transform, vec4 tint) {
-    assert(texture_quad_batch != NULL && "texture_quad_batch is null, was setup_textures() called?");
-    batch_draw_texture(texture_quad_batch, id, transform, tint);
-}
-
-void batch_draw_texture(RenderBatch* batch, Texture texture, mat4 transform, vec4 color) {
-    TextureQuadBatchData* batch_data = batch->data;
-    uint32_t vertex_add = 6; 
-
-    if(batch_needs_flush(batch, vertex_add) || batch_data->texture_index >= max_textures) 
-        flush_batch(batch); 
- 
-    uint32_t tex_id = batch_data->texture_index++;
-    glBindTextureUnit(tex_id, texture);
-
-    for(int i = 0; i < vertex_add; i++) {
-        TextureQuadVertex* vertex = batch->vertex_ptr;
-
-        vertex->Position = vec3Transform(quad_vertex_positions[i], transform);
-        vertex->Tint = color;
-        vertex->TexCoord = texture_quad_texcoords[i];
-        vertex->TexID = tex_id;
-
-        batch->vertex_ptr += batch->vertex_size;
-        batch->vertex_count++;
-    }
-}
-
-void texture_flush_callback(RenderBatch* batch) {
-    TextureQuadBatchData* data = batch->data;
-    data->texture_index = 0;
-}
-
-RenderBatch* create_texture_quad_batch() {
-    RenderBatch* texture_quad_batch = create_batch(sizeof(TextureQuadVertex), MAX_VERTICES);
-    texture_quad_batch->shader = load_shader_program(
-            compile_shader("assets/texture.vert", GL_VERTEX_SHADER),
-            compile_shader("assets/texture.frag", GL_FRAGMENT_SHADER), 0);
-
-    texture_quad_batch->data = calloc(sizeof(TextureQuadBatchData), 1);
-    texture_quad_batch->flush_callback = &texture_flush_callback;
-
-    batch_add_attrib(texture_quad_batch, (VertexAttrib){ 
-            .type = GL_FLOAT, .size = sizeof(float), .count = 3 
-    }); // pos
-    
-    batch_add_attrib(texture_quad_batch, (VertexAttrib){ 
-            .type = GL_FLOAT, .size = sizeof(float), .count = 4 
-    }); // color
-
-    batch_add_attrib(texture_quad_batch, (VertexAttrib){ 
-            .type = GL_FLOAT, .size = sizeof(float), .count = 2 
-    }); // texcoord
-
-    batch_add_attrib(texture_quad_batch, (VertexAttrib){ 
-            .type = GL_FLOAT, .size = sizeof(float), .count = 1 
-    }); // texid
-
-    batch_bind_attribs(texture_quad_batch);
-
-    return texture_quad_batch;
-}
+#include <textures.h>
+#include <stb_image.h>
+#include <opengl.h>
+#include <stdio.h>
+#include <assert.h>
+
+RenderBatch* texture_quad_batch = NULL;
+int max_textures;
+
+void setup_textures() {
+    texture_quad_batch = create_texture_quad_batch();
+
+    glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_textures);
+    if(max_textures > 32) max_textures = 32;
+}
+
+uint32_t load_texture(const char* path) {
+    uint32_t id;
+    glCreateTextures(GL_TEXTURE_2D, 1, &id); 
+
+    uint8_t default_texture[] = { 
+	    0, 0, 0, 255, 	255, 0, 255, 255,
+	    255, 0, 255, 255,	0, 0, 0, 255 };
+
+    int width = 2, height = 2, channels;
+    uint8_t* pixels = stbi_load(path, &width, &height, &channels, 4);
+    if(pixels) {
+	glTextureParameteri(id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    	glTextureParameteri(id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    	glTextureParameteri(id, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    	glTextureParameteri(id, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+        glTextureStorage2D(id, 1, GL_RGBA8, width, height);
+        glTextureSubImage2D(id, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+        printf("[Texture %d, %s] Loaded successfully (%dx%d, %d channels)\n", id, path, width, height, channels);
+    } else {
+	glTextureParameteri(id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    	glTextureParameteri(id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    	glTextureParameteri(id, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    	glTextureParameteri(id, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+	glTextureStorage2D(id, 1, GL_RGBA8, width, height);
+	glTextureSubImage2D(id, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, default_texture);
+        printf("[Texture %d, %s] Failed to load image data: %s\n", id, path, stbi_failure_reason());
+    }
+
+    return id;
+}
+
+vec2 texture_quad_texcoords[] = {
+    { 0.0f, 1.0f },
+    { 0.0f, 0.0f },
+    { 1.0f, 0.0f },
+        
+    { 0.0f, 1.0f },
+    { 1.0f, 0.0f },
+    { 1.0f, 1.0f }
+};
+
+void draw_texture(Texture id, vec2 pos, vec2 size, vec4 tint) { 
+    mat4 transform = mat4Multiply(
+            mat4Scale(size.x, size.y, 1.0f),
+            mat4Translate(pos.x, pos.y, 0.0f));
+
+    draw_texture_trans(id, transform, tint);
+}
+
+void draw_texture_trans(Texture id, mat4 transform, vec4 tint) {
+    assert(texture_quad_batch != NULL && "texture_quad_batch is null, was setup_textures() called?");
+    batch_draw_texture(texture_quad_batch, id, transform, tint);
+}
+
+void batch_draw_texture(RenderBatch* batch, Texture texture, mat4 transform, vec4 color) {
+    TextureQuadBatchData* batch_data = batch->data;
+    uint32_t vertex_add = 6; 
+
+    if(batch_needs_flush(batch, vertex_add) || batch_data->texture_index >= max_textures) 
+        flush_batch(batch); 
+ 
+    uint32_t tex_id = batch_data->texture_index++;
+    glBindTextureUnit(tex_id, texture);
+
+    for(int i = 0; i < vertex_add; i++) {
+        TextureQuadVertex* vertex = batch->vertex_ptr;
+
+        vertex->Position = vec3Transform(quad_vertex_positions[i], transform);
+        vertex->Tint = color;
+        vertex->TexCoord = texture_quad_texcoords[i];
+        vertex->TexID = tex_id;
+
+        batch->vertex_ptr += batch->vertex_size;
+        batch->vertex_count++;
+    }
+}
+
+void texture_flush_callback(RenderBatch* batch) {
+    TextureQuadBatchData* data = batch->data;
+    data->texture_index = 0;
+}
+
+RenderBatch* create_texture_quad_batch() {
+    RenderBatch* texture_quad_batch = create_batch(sizeof(TextureQuadVertex), MAX_VERTICES);
+    texture_quad_batch->shader = load_shader_program(
+            compile_shader("assets/texture.vert", GL_VERTEX_SHADER),
+            compile_shader("assets/texture.frag", GL_FRAGMENT_SHADER), 0);
+
+    texture_quad_batch->data = calloc(sizeof(TextureQuadBatchData), 1);
+    texture_quad_batch->flush_callback = &texture_flush_callback;
+
+    batch_add_attrib(texture_quad_batch, (VertexAttrib){ 
+            .type = GL_FLOAT, .size = sizeof(float), .count = 3 
+    }); // pos
+    
+    batch_add_attrib(texture_quad_batch, (VertexAttrib){ 
+            .type = GL_FLOAT, .size = sizeof(float), .count = 4 
+    }); // color
+
+    batch_add_attrib(texture_quad_batch, (VertexAttrib){ 
+            .type = GL_FLOAT, .size = sizeof(float), .count = 2 
+    }); // texcoord
+
+    batch_add_attrib(texture_quad_batch, (VertexAttrib){ 
+            .type = GL_FLOAT, .size = sizeof(float), .count = 1 
+    }); // texid
+
+    batch_bind_attribs(texture_quad_batch);
+
+    return texture_quad_batch;
+}