diff --git a/src/camera.h b/src/camera.h
new file mode 100644
index 0000000..5bd9f4e
--- /dev/null
+++ b/src/camera.h
@@ -0,0 +1,24 @@
+#ifndef _fencer_camera_h
+#define _fencer_camera_h
+
+#include "vmath.h"
+#include <SDL2/SDL_rect.h>
+
+struct Camera {
+    Vector centre;
+    float width;
+};
+
+typedef struct Camera Camera;
+
+// current world location of camera
+extern Camera g_camera;
+
+extern void camera_init();
+// generate a screen-space rectangle that is exactly 1x1 in world units.
+// With it's centre on the world origin.
+extern SDL_FRect camera_calculate_world_unit_rect(Camera* self);
+extern SDL_FRect camera_to_world_space(Camera* self, SDL_FRect* camera_space);
+extern SDL_FRect camera_world_to_camera_space(Camera* self, SDL_FRect* world_space);
+
+#endif // !_fencer_camera_h
diff --git a/src/fencer.c b/src/fencer.c
index 970be49..2777243 100644
--- a/src/fencer.c
+++ b/src/fencer.c
@@ -5,7 +5,7 @@ int main(int argc, char* argv[]) {
     struct ProgramSettings config = {
         .target_fps = 0, // unbounded speed
         .title = "fencer",
-        .view_resolution = {160, 140}
+        .view_resolution = {1920, 1080}
     };
     return program_run(&config);
 }
diff --git a/src/program.c b/src/program.c
index 9109b0d..4cf1be6 100644
--- a/src/program.c
+++ b/src/program.c
@@ -1,14 +1,19 @@
 #include "program.h"
 #include <SDL2/SDL_video.h>
+#include <SDL2/SDL_image.h>
+#include "tilemap.h"
+
+SDL_Window* g_window;
+double g_delta_time;
+double g_frame_start;
 
-struct Program g_program;
 
 #define INITFLAGS SDL_INIT_EVENTS | SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMECONTROLLER
 
 int program_run(const struct ProgramSettings* settings) {
     SDL_Init(INITFLAGS);
 
-    g_program.window = SDL_CreateWindow(
+    g_window = SDL_CreateWindow(
         settings->title,
         SDL_WINDOWPOS_CENTERED_DISPLAY(0),
         SDL_WINDOWPOS_CENTERED_DISPLAY(0),
@@ -16,45 +21,55 @@ int program_run(const struct ProgramSettings* settings) {
         settings->view_resolution.y,
         SDL_WINDOW_FULLSCREEN | SDL_WINDOW_RESIZABLE);
 
-    g_program.render_context = render_init(&g_program, settings);
+    render_init(g_window, settings);
+
+    struct Tilemap map = tilemap_load("resources/box.tilemap.xml");
+    struct Tileset set = {
+        .texture = IMG_LoadTexture(g_renderer, "resources/ts_castle.png"),
+        .tile_size = {32, 32},
+        .shear = 10
+    };
+    SDL_QueryTexture(set.texture, NULL, NULL, &set.texture_resolution.x, &set.texture_resolution.y);
+    map.tileset = set;
 
     for(;;) {
         SDL_Delay(1);
-        program_handle_events(&g_program);
-        render_present(g_program.render_context);
+        program_handle_events();
+        tilemap_render(&map);
+        render_present();
     }
 
     return 1;
 }
 
-void program_quit(struct Program* self) {
-    render_destroy(self->render_context);
-    SDL_DestroyWindow(self->window);
+void program_quit() {
+    render_clean();
+    SDL_DestroyWindow(g_window);
     SDL_Quit();
     exit(0);
 }
 
-void program_handle_events(struct Program* self) {
+void program_handle_events() {
     SDL_Event event;
     while(SDL_PollEvent(&event)) {
         switch(event.type) {
             default: break;
             case SDL_WINDOWEVENT:
-                if(event.window.windowID == SDL_GetWindowID(self->window)) {
-                    program_handle_windowevent(self, &event.window);
+                if(event.window.windowID == SDL_GetWindowID(g_window)) {
+                    program_handle_windowevent(&event.window);
                 }
                 break;
             case SDL_QUIT:
-                program_quit(self);
+                program_quit();
                 break;
         }
     }
 }
 
-void program_handle_windowevent(struct Program* self, SDL_WindowEvent* event) {
+void program_handle_windowevent(SDL_WindowEvent* event) {
     switch(event->type) {
         default:
-            render_handle_resize(self->render_context);
+            render_handle_resize();
             break;
     }
 }
diff --git a/src/program.h b/src/program.h
index d3f8e85..b9ae660 100644
--- a/src/program.h
+++ b/src/program.h
@@ -6,24 +6,19 @@
 #include "vmath.h"
 #include "render.h"
 
-extern struct Program g_program;
-
 struct ProgramSettings {
     const char* title;
     IVector view_resolution;
     int target_fps;
 };
 
-struct Program {
-    SDL_Window* window;
-    struct RenderContext* render_context;
-    double delta_time;
-    double start_frame;
-};
+extern SDL_Window* g_window;
+extern double g_delta_time;
+extern double g_frame_start;
 
 extern int program_run(const struct ProgramSettings* settings);
-extern void program_quit(struct Program* self);
-extern void program_handle_events(struct Program* self);
-extern void program_handle_windowevent(struct Program* self, SDL_WindowEvent* event);
+extern void program_quit();
+extern void program_handle_events();
+extern void program_handle_windowevent(SDL_WindowEvent* event);
 
 #endif // !_fencer_program_h
diff --git a/src/render.c b/src/render.c
index 7d8e0be..c9b5507 100644
--- a/src/render.c
+++ b/src/render.c
@@ -3,83 +3,84 @@
 #include <SDL2/SDL_pixels.h>
 #include <SDL2/SDL_render.h>
 
-struct RenderContext* render_init(const struct Program* program, const struct ProgramSettings* settings) {
-    struct RenderContext* self = malloc(sizeof(struct RenderContext));
+SDL_Renderer* g_renderer;
+SDL_Texture* g_render_target;
+SDL_Rect g_render_area;
+IVector g_render_resolution;
 
+void render_init(SDL_Window* window, const struct ProgramSettings* settings) {
     // create renderer, needs to be able to target textures, preferably hardware accelerated
-    self->renderer = SDL_CreateRenderer(program->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
+    g_renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
     // create render target texture
-    self->render_target = SDL_CreateTexture(
-        self->renderer,
+    g_render_target = SDL_CreateTexture(
+        g_renderer,
         SDL_PIXELFORMAT_ARGB32,
         SDL_TEXTUREACCESS_TARGET,
         settings->view_resolution.x, settings->view_resolution.y);
 
     // store the desired view resolution
-    self->render_resolution = settings->view_resolution;
+    g_render_resolution = settings->view_resolution;
 
     // get the destination area for rendering the target onto the window
-    render_calculate_render_area(self);
+    render_calculate_render_area();
 
     // clear target black
-    SDL_SetRenderTarget(self->renderer, self->render_target);
-    SDL_SetRenderDrawColor(self->renderer, 0, 0, 0, 255);
-    SDL_RenderClear(self->renderer);
-
-    return self;
+    SDL_SetRenderTarget(g_renderer, g_render_target);
+    SDL_SetRenderDrawColor(g_renderer, 0, 0, 0, 255);
+    SDL_RenderClear(g_renderer);
 }
 
-void render_destroy(struct RenderContext* self) {
+void render_clean() {
     // clear up allocated SDL objects
-    SDL_DestroyTexture(self->render_target);
-    SDL_DestroyRenderer(self->renderer);
+    SDL_DestroyTexture(g_render_target);
+    SDL_DestroyRenderer(g_renderer);
 }
 
-void render_present(struct RenderContext* self) {
+void render_present() {
     // clear window white
-    SDL_SetRenderTarget(self->renderer, NULL);
-    SDL_SetRenderDrawColor(self->renderer, 0, 0, 0, 255);
-    SDL_RenderClear(self->renderer);
+    SDL_SetRenderTarget(g_renderer, NULL);
+    SDL_SetRenderDrawColor(g_renderer, 0, 0, 0, 255);
+    SDL_RenderClear(g_renderer);
     // copy render target
-    SDL_Rect source_rect = {0, 0, self->render_resolution.x, self->render_resolution.y};
-    SDL_RenderCopy(self->renderer, self->render_target, &source_rect, &self->render_area);
-    SDL_RenderPresent(self->renderer);
+    SDL_Rect source_rect = {0, 0, g_render_resolution.x, g_render_resolution.y};
+    SDL_RenderCopy(g_renderer, g_render_target, &source_rect, &g_render_area);
+    SDL_RenderPresent(g_renderer);
     // clear render target
-    SDL_SetRenderTarget(self->renderer, self->render_target);
-    SDL_SetRenderDrawColor(self->renderer, 0, 0, 0, 255);
-    SDL_RenderClear(self->renderer);
+    SDL_SetRenderTarget(g_renderer, g_render_target);
+    SDL_SetRenderDrawColor(g_renderer, 0, 0, 0, 255);
+    SDL_RenderClear(g_renderer);
 }
 
-void render_calculate_render_area(struct RenderContext* self) {
+void render_calculate_render_area() {
     // get aspect ratios of both the window and the rendertexture
-    IVector window_resolution = render_get_window_size(self);
+    IVector window_resolution = render_get_window_size();
     float window_aspect = (float)window_resolution.x / (float)window_resolution.y;
-    float target_aspect = (float)self->render_resolution.x / (float)self->render_resolution.y;
+    float target_aspect = (float)g_render_resolution.x / (float)g_render_resolution.y;
 
     // calculate the largest area that will fit the entire rendertexture into the window space
-    self->render_area = (SDL_Rect) {0, 0, window_resolution.x, window_resolution.y};
+    g_render_area = (SDL_Rect) {0, 0, window_resolution.x, window_resolution.y};
     if(window_aspect <= target_aspect) {
-        self->render_area.h = window_resolution.x / target_aspect;
-        self->render_area.y = (window_resolution.y - self->render_area.h) / 2;
+        g_render_area.h = window_resolution.x / target_aspect;
+        g_render_area.y = (window_resolution.y - g_render_area.h) / 2;
     } else {
-        self->render_area.w = window_resolution.y * target_aspect;
-        self->render_area.x += (window_resolution.x - self->render_area.w) / 2;
+        g_render_area.w = window_resolution.y * target_aspect;
+        g_render_area.x += (window_resolution.x - g_render_area.w) / 2;
     }
 }
 
-IVector render_get_window_size(struct RenderContext* self) {
+IVector render_get_window_size() {
     // select window as target (store target to reset)
-    SDL_Texture* target = SDL_GetRenderTarget(self->renderer);
-    SDL_SetRenderTarget(self->renderer, NULL);
+    SDL_Texture* target = SDL_GetRenderTarget(g_renderer);
+    SDL_SetRenderTarget(g_renderer, NULL);
     // fetch output size (= window size) of renderer
     int window_width, window_height;
-    SDL_GetRendererOutputSize(self->renderer, &window_width, &window_height);
+    SDL_GetRendererOutputSize(g_renderer, &window_width, &window_height);
     // reset render target
-    SDL_SetRenderTarget(self->renderer, target);
+    SDL_SetRenderTarget(g_renderer, target);
     // construct IVector from fetched output size
     return (IVector){window_width, window_height};
 }
 
-void render_handle_resize(struct RenderContext* self) {
-    render_calculate_render_area(self);
+void render_handle_resize() {
+    render_calculate_render_area();
 }
diff --git a/src/render.h b/src/render.h
index 240eecd..cbfc1d1 100644
--- a/src/render.h
+++ b/src/render.h
@@ -5,32 +5,25 @@
 #include <SDL2/SDL_rect.h>
 #include "vmath.h"
 
-struct Program;
 struct ProgramSettings;
 
-struct RenderContext {
-    // renderer created from window in Program passed to render_init
-    SDL_Renderer* renderer;
+// renderer created from window in Program passed to render_init
+extern SDL_Renderer* g_renderer;
+// render target
+extern SDL_Texture* g_render_target;
+// area of window to render render_target to
+extern SDL_Rect g_render_area;
+// size of render_target
+extern IVector g_render_resolution;
 
-    // render target
-    SDL_Texture* render_target;
-    // area of window to render render_target to
-    SDL_Rect render_area;
+extern void render_init(SDL_Window* window, const struct ProgramSettings* settings);
+extern void render_clean();
+extern void render_present();
 
-    // size of render_target
-    IVector render_resolution;
-
-    // current world location of camera
-    IVector camera_offset;
-};
-
-extern struct RenderContext* render_init(const struct Program* program, const struct ProgramSettings* settings);
-extern void render_destroy(struct RenderContext* self);
-extern void render_present(struct RenderContext* self);
-
-extern void render_calculate_render_area(struct RenderContext* self);
-extern IVector render_get_window_size(struct RenderContext* self);
-extern void render_handle_resize(struct RenderContext* self);
+extern SDL_FRect render_calculate_unit_rect();
+extern void render_calculate_render_area();
+extern IVector render_get_window_size();
+extern void render_handle_resize();
 
 
 #endif // !_fencer_render_h
diff --git a/src/tilemap.c b/src/tilemap.c
index 58d7595..496af9b 100644
--- a/src/tilemap.c
+++ b/src/tilemap.c
@@ -1,11 +1,9 @@
 #include "tilemap.h"
-#include "libxml/threads.h"
-#include "libxml/tree.h"
-#include "libxml/xmlstring.h"
+#include "camera.h"
 #include "program.h"
-#include <SDL2/SDL_image.h>
-#include <libxml/xinclude.h>
 #include <stdio.h>
+#include <libxml/tree.h>
+#include <SDL2/SDL_image.h>
 
 #define XML(__str) (const xmlChar*)__str
 
@@ -135,7 +133,7 @@ struct Tileset tileset_load(const char* filename) {
         }
     } while(n != ',');
 
-    self.texture = IMG_LoadTexture(g_program.render_context->renderer, texture_path);
+    self.texture = IMG_LoadTexture(g_renderer, texture_path);
     SDL_QueryTexture(self.texture, NULL, NULL, &self.texture_resolution.x, &self.texture_resolution.y);
     self.shear = floor((float)self.texture_resolution.x / self.tile_size.x);
 
@@ -169,14 +167,18 @@ SDL_Rect tileset_index_to_rect(struct Tileset* self, size_t index) {
     return rect;
 }
 
-void tilemap_render(struct Tilemap* self, struct RenderContext* render) {
+void tilemap_render(struct Tilemap* self) {
     size_t num_tiles = self->dimensions.x * self->dimensions.y;
+
     SDL_Rect source_rect = {0, 0, self->tileset.tile_size.x, self->tileset.tile_size.y};
-    SDL_Rect dest_rect = source_rect;
+    SDL_FRect dest_rect = { .w = 1, .h = 1 };
+
     for(int i = 0; i < num_tiles; ++i) {
         source_rect = tileset_index_to_rect(&self->tileset, self->tiles[i]);
+
         dest_rect.x = (i % self->dimensions.x) * dest_rect.w;
-        dest_rect.y = (i / self->dimensions.y) * dest_rect.h;
-        SDL_RenderCopy(render->renderer, self->tileset.texture, &source_rect, &dest_rect);
+        dest_rect.y = (float)floor((float)i / self->dimensions.y) * dest_rect.h;
+
+        SDL_RenderCopyF(g_renderer, self->tileset.texture, &source_rect, &dest_rect);
     }
 }
\ No newline at end of file
diff --git a/src/tilemap.h b/src/tilemap.h
index 6cc11a5..f8fdd5e 100644
--- a/src/tilemap.h
+++ b/src/tilemap.h
@@ -22,6 +22,6 @@ extern struct Tilemap tilemap_load(const char* tilemap_file);
 extern struct Tileset tileset_load(const char* filename);
 extern SDL_Rect tileset_index_to_rect(struct Tileset* self, size_t index);
 
-extern void tilemap_render(struct Tilemap* map, struct RenderContext* render);
+extern void tilemap_render(struct Tilemap* map);
 
 #endif // !_fencer_tilemap_h