diff --git a/bin/Makefile b/bin/Makefile
index 5d51494..1416a00 100644
--- a/bin/Makefile
+++ b/bin/Makefile
@@ -12,8 +12,9 @@ DATAFILES = $(shell find ../gfx/ ../dat/ -name '*.png' -o -name '*.xml' -print)
 CLUA = -I/usr/include/lua5.1
 CSDL = `sdl-config --cflags`
 CXML = `xml2-config --cflags`
+CTTF = `freetype-config --cflags`
 CGL  =
-CFLAGS = -Wall $(CLUA) $(CSDL) $(CXML) $(CGL) $(VERSION)
+CFLAGS = -Wall $(CLUA) $(CSDL) $(CXML) $(CTTF) $(CGL) $(VERSION)
 ifdef DEBUG
 CFLAGS += -g3 -DDEBUG
 else
@@ -23,8 +24,9 @@ endif
 LDLUA = -llua5.1
 LDSDL = `sdl-config --libs` -lSDL_image
 LDXML = `xml2-config --libs`
+LDTTF = `freetype-config --libs`
 LDGL	= -lGL
-LDFLAGS = -lm $(LDLUA) $(LDSDL) $(LDXML) $(LDGL)
+LDFLAGS = -lm $(LDLUA) $(LDSDL) $(LDXML) $(LDTTF) $(LDGL)
 
 %.o: ../src/%.c
 	@gcc -c $(CFLAGS) -o $@ $<
diff --git a/src/main.c b/src/main.c
index 99ebd0a..b74f273 100644
--- a/src/main.c
+++ b/src/main.c
@@ -15,23 +15,27 @@
 #include "joystick.h"
 #include "space.h"
 #include "rng.h"
+#include "ai.h"
 #include "pilot.h"
 
 #define CONF_FILE "conf"
 
+static gl_font fdefault;
+
 static int quit = 0;
 
 static unsigned int time = 0;
 
 // Prototypes.
 
-void print_usage(char** argv);
+static void print_usage(char** argv);
+static void display_fps(const double dt);
 
 // Update.
 static void update_all(void);
 
 // Usage.
-void print_usage(char** argv) {
+static void print_usage(char** argv) {
   LOG("USAGE: %s [-f] [-j n | -J s] [-hv]", argv[0]);
   LOG("Options are:");
   LOG("\t-f   - Fullscreen");
@@ -120,6 +124,12 @@ int main(int argc, char** argv) {
       joystick_use(indjoystick);
   }
 
+  // Misc.
+  if(ai_init())
+    WARN("Error initializing AI");
+
+  gl_fontInit(&fdefault, "/usr/share/fonts/truetype/freefont/FreeSans.ttf", 16);
+
   // Data loading.
   ships_load();
 
@@ -144,9 +154,13 @@ int main(int argc, char** argv) {
     update_all();
   }
   space_exit();
+
   // Unload data.
   pilots_free();
   ships_free();
+
+  gl_freeFont(&fdefault);
+
   // Exit subsystems.
   joystick_exit();
   gl_exit(); // Kills video output.
@@ -165,6 +179,22 @@ static void update_all(void) {
   space_render(dt);
   pilots_update(dt);
 
+  display_fps(dt);
+
   SDL_GL_SwapBuffers();
 }
 
+static double fps = 0.;
+static double fps_cur = 0.;
+static double fps_dt = 1.;
+static void display_fps(const double dt) {
+  fps_dt += dt;
+  fps_cur += 1.;
+  if(fps_dt > 1.) {
+    fps = fps_cur;
+    fps_dt = fps_cur = 0.;
+  }
+  Vec2 pos = { .x = 10., .y = (double)(gl_screen.h-20) };
+  gl_print(&fdefault, &pos, "%3.2f", fps);
+}
+
diff --git a/src/opengl.c b/src/opengl.c
index 32049dc..9c3501b 100644
--- a/src/opengl.c
+++ b/src/opengl.c
@@ -1,6 +1,12 @@
 #include <SDL.h>
 #include <SDL_image.h>
+#include <ft2build.h>
+#include <freetype/freetype.h>
+#include <freetype/ftglyph.h>
 #include <math.h>
+#include <stdarg.h>
+#include <string.h>
+
 #include "def.h"
 #include "log.h"
 #include "opengl.h"
@@ -14,7 +20,25 @@ gl_info gl_screen;
 // Our precious camera.
 Vec2* gl_camera;
 
+// Misc.
 static int flip_surface(SDL_Surface* surface);
+static int pot(int n);
+// gl_texture.
+static GLuint gl_loadSurface(SDL_Surface* surface, int* rw, int* rh);
+// Gl font.
+static void gl_fontMakeDList(FT_Face face, char ch, GLuint list_base, GLuint* tex_base);
+
+// ================
+// MISC!
+// ================
+
+// Get me the closest power of two plox.
+static int pot(int n) {
+  int i = 1;
+  while( i < n)
+    i<<=1;
+  return i;
+}
 
 // Flips the surface vertically. Return 0 on success.
 static int flip_surface(SDL_Surface* surface) {
@@ -42,38 +66,22 @@ static int flip_surface(SDL_Surface* surface) {
   return 0;
 }
 
-// Load the SDL_Surface to an opengl texture.
-gl_texture* gl_loadImage(SDL_Surface* surface) {
+// ================
+// TEXTURE!
+// ================
+
+// Returns the texture ID.
+// Stores real sizes in rw/rh (from POT padding).
+static GLuint gl_loadSurface(SDL_Surface* surface, int* rw, int* rh) {
+  GLuint texture;
   SDL_Surface* tmp;
   Uint32 saved_flags;
   Uint8  saved_alpha;
-  int potw, poth;
-
-  // Set up the texture defaults.
-  gl_texture* texture = MALLOC_L(gl_texture);
-  texture->w = (FP)surface->w;
-  texture->h = (FP)surface->h;
-  texture->sx = 1.;
-  texture->sy = 1.;
-
-  // Ensure size is power of two.
-  potw = surface->w;
-  if((potw &(potw-1)) != 0) {
-    potw = 1;
-    while(potw < surface->w)
-      potw <<= 1;
-  }
-  texture->rw = potw;
   
-  poth = surface->h;
-  if((poth &(poth-1)) != 0) {
-    poth = 1;
-    while(poth < surface->h)
-      poth <<= 1;
-  }
-  texture->rh = poth;
+  if(rw)*rw = pot(surface->w);
+  if(rh)*rh = pot(surface->h);
 
-  if(surface->w != potw || surface->h != poth) {
+  if(surface->w != *rw || surface->h != *rh) {
     // Size isn't original.
     SDL_Rect rtemp;
     rtemp.x = rtemp.y = 0;
@@ -88,14 +96,14 @@ gl_texture* gl_loadImage(SDL_Surface* surface) {
 
     // Create the temp POT surface.
     tmp = SDL_CreateRGBSurface(SDL_SRCCOLORKEY,
-          texture->rw, texture->rh, surface->format->BytesPerPixel*8, RGBMASK);
+          *rw, *rh, surface->format->BytesPerPixel*8, RGBMASK);
     if(tmp == NULL) {
       WARN("Unable to create POT surface %s", SDL_GetError());
-      return NULL;
+      return 0;
     }
     if(SDL_FillRect(tmp, NULL, SDL_MapRGBA(surface->format, 0, 0, 0, SDL_ALPHA_TRANSPARENT))) {
       WARN("Unable to fill rect: %s", SDL_GetError());
-      return NULL;
+      return 0;
     }
 
     SDL_BlitSurface(surface, &rtemp, tmp, &rtemp);
@@ -109,14 +117,14 @@ gl_texture* gl_loadImage(SDL_Surface* surface) {
 
     // Create the temp POT surface.
     tmp = SDL_CreateRGBSurface(SDL_SRCCOLORKEY,
-          texture->rw, texture->rh, surface->format->BytesPerPixel*8, RGBMASK);
+          *rw, *rh, surface->format->BytesPerPixel*8, RGBMASK);
     if(tmp == NULL) {
       WARN("Unable to create POT surface %s", SDL_GetError());
-      return NULL;
+      return 0;
     }
     if(SDL_FillRect(tmp, NULL, SDL_MapRGBA(surface->format, 0, 0, 0, SDL_ALPHA_TRANSPARENT))) {
       WARN("Unable to fill rect: %s", SDL_GetError());
-      return NULL;
+      return 0;
     }
 
     SDL_BlitSurface(surface, &rtemp, tmp, &rtemp);
@@ -129,8 +137,8 @@ gl_texture* gl_loadImage(SDL_Surface* surface) {
       SDL_SetAlpha(surface, saved_flags, saved_alpha);
   }
 
-  glGenTextures(1, &texture->texture); // Create the texure.
-  glBindTexture(GL_TEXTURE_2D, texture->texture); // Load the texture.
+  glGenTextures(1, &texture); // Create the texure.
+  glBindTexture(GL_TEXTURE_2D, texture); // Load the texture.
 
   // Filtering, LINEAR is better for scaling, nearest looks nicer, LINEAR
   // also seems to create a bit of artifacts around the edges.
@@ -144,8 +152,24 @@ gl_texture* gl_loadImage(SDL_Surface* surface) {
   SDL_UnlockSurface(surface);
   SDL_FreeSurface(surface);
 
+  return texture;
+}
+
+// Load the SDL_Surface to an openGL texture.
+gl_texture* gl_loadImage(SDL_Surface* surface) {
+  int rw, rh;
+
+  // Set up the texture defaults.
+  gl_texture* texture = MALLOC_L(gl_texture);
+  texture->w = (FP)surface->w;
+  texture->h = (FP)surface->h;
   texture->sx = 1.;
   texture->sy = 1.;
+
+  texture->texture = gl_loadSurface(surface, &rw, &rh);
+
+  texture->rw = (FP)rw;
+  texture->rh = (FP)rh;
   texture->sw = texture->w;
   texture->sh = texture->h;
   
@@ -190,13 +214,17 @@ gl_texture* gl_newSprite(const char* path, const int sx, const int sy) {
 }
 
 // Free the texture.
-void gl_free(gl_texture* texture) {
+void gl_freeTexture(gl_texture* texture) {
   glDeleteTextures(1, &texture->texture);
   free(texture);
 }
 
+// ================
+// BLITTING!
+// ================
+
 // Blit the sprite at given position.
-void gl_blitSprite(gl_texture* sprite, Vec2* pos, const int sx, const int sy) {
+void gl_blitSprite(const gl_texture* sprite, const Vec2* pos, const int sx, const int sy) {
   // Don't bother drawing if offscreen -- waste of cycles.
   if(fabs(pos->x - gl_camera->x) > gl_screen.w / 2 + sprite->sw / 2 ||
         fabs(pos->y-gl_camera->y) > gl_screen.h / 2 + sprite->sh / 2)
@@ -232,7 +260,7 @@ void gl_blitSprite(gl_texture* sprite, Vec2* pos, const int sx, const int sy) {
 }
 
 // Just straight out blit the thing at position.
-void gl_blitStatic(gl_texture* texture, Vec2* pos) {
+void gl_blitStatic(const gl_texture* texture, const Vec2* pos) {
   glMatrixMode(GL_PROJECTION);
   glPushMatrix(); // Set up translation matrix.
   glTranslatef(pos->x - (FP)gl_screen.w/2., pos->y - (FP)gl_screen.h/2., 0);
@@ -255,10 +283,156 @@ void gl_blitStatic(gl_texture* texture, Vec2* pos) {
 }
 
 // Bind our precious camera to a vector.
-void gl_bindCamera(Vec2* pos) {
-  gl_camera = pos;
+void gl_bindCamera(const Vec2* pos) {
+  gl_camera = (Vec2*)pos;
 }
 
+// Print text on screen! YES!!!! Just like printf! But different!
+void gl_print(const gl_font* ft_font, Vec2* pos, const char* fmt, ...) {
+  //float h = ft_font->h / .63; // Slightly increases font size.
+  char text[256];
+  va_list ap;
+  //int i;
+
+  if(fmt == NULL)
+    *text = 0;
+  else {
+    // convert the symbols to text.
+    va_start(ap, fmt);
+    vsprintf(text, fmt, ap);
+    va_end(ap);
+  }
+
+  glListBase(ft_font->list_base);
+
+  glMatrixMode(GL_PROJECTION);
+  //for(i = 0; i < strlen(text); i++) {
+    glPushMatrix();
+    glTranslatef(pos->x - (FP)gl_screen.w/2., pos->y - (FP)gl_screen.h/2., 0);
+    glCallLists(strlen(text), GL_UNSIGNED_BYTE, &text);
+    glPopMatrix();
+  //}
+}
+
+// ================
+// FONT!
+// ================
+static void gl_fontMakeDList(FT_Face face, char ch, GLuint list_base, GLuint* tex_base) {
+  FT_Glyph glyph;
+  FT_Bitmap bitmap;
+  GLubyte* expanded_data;
+  int w, h;
+  int i, j;
+
+  if(FT_Load_Glyph(face, FT_Get_Char_Index(face, ch), FT_LOAD_DEFAULT))
+    WARN("FT_Load_Glyph failed");
+
+  if(FT_Get_Glyph(face->glyph, &glyph))
+    WARN("FT_Ge_Glyph failed");
+
+  // Convert your glyph to a bitmap.
+  FT_Glyph_To_Bitmap(&glyph, ft_render_mode_normal, 0, 1);
+  FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph;
+
+  bitmap = bitmap_glyph->bitmap; // To simplify.
+
+  // Need the POT wrapping for GL.
+  w = pot(bitmap.width);
+  h = pot(bitmap.rows);
+
+  // Memory for textured data.
+  // Bitmap is useing two channels, one for luminosity and one for alpha.
+  expanded_data = (GLubyte*)malloc(sizeof(GLubyte)*2*w*h);
+  for(j = 0; j < h; j++) {
+    for(i = 0; i < w; i++) {
+      expanded_data[2*(i+j*w)] = expanded_data[2*(i+j*w)+1] =
+          (i >= bitmap.width || j >= bitmap.rows) ? 0 : bitmap.buffer[i + bitmap.width*j];
+    }
+  }
+  // Create the GL texture.
+  glBindTexture(GL_TEXTURE_2D, tex_base[(int)ch]);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded_data);
+
+  free(expanded_data); // No need for this now.
+
+  // Create the display lists.
+  glNewList(list_base+ch, GL_COMPILE);
+
+  glBindTexture(GL_TEXTURE_2D, tex_base[(int)ch]);
+
+  glPushMatrix();
+
+  // Corrects a spacing flaw between letters.
+  glTranslatef(bitmap_glyph->left, 0,0);
+
+  // Downwards correction for letters like g or y.
+  glTranslatef(0, bitmap_glyph->top-bitmap.rows,0);
+
+  // Take the opengl POT wrapping into account.
+  FP x = (FP)bitmap.width/(FP)w;
+  FP y = (FP)bitmap.rows/(FP)h;
+
+  // Draw the texture mapped quad.
+  glBegin(GL_QUADS);
+    glTexCoord2d(0, 0);
+      glVertex2f(0, bitmap.rows);
+    glTexCoord2d(x, y);
+      glVertex2f(bitmap.width, 0);
+    glTexCoord2d(x, 0);
+      glVertex2f(bitmap.width, bitmap.rows);
+  glEnd();
+
+  glPopMatrix();
+  glTranslatef(face->glyph->advance.x >> 6, 0,0);
+
+  // End of the display list.
+  glEndList();
+}
+
+void gl_fontInit(gl_font* font, const char* fname, unsigned int h) {
+  font->textures = malloc(sizeof(GLuint)*128);
+  font->h = h;
+
+  // Create a FreeType font library.
+  FT_Library library;
+  if(FT_Init_FreeType(&library)) {
+    WARN("FT_Init_FreeType failed");
+  }
+
+  // Objects that freetype uses to store font info.
+  FT_Face face;
+  if(FT_New_Face(library, fname, 0, &face))
+    WARN("FT_New_Face failed loading library from %s", fname);
+
+  // FreeType is pretty nice and measures using 1/64 of a pixel, therfore expand.
+  FT_Set_Char_Size(face, h << 6, h << 6, 96, 96);
+
+  // Have OpenGL allocate space for the textures / display lists.
+  font->list_base = glGenLists(128);
+  glGenTextures(128, font->textures);
+
+  // Create each of the font display lists.
+  unsigned char i;
+  for(i = 0; i < 128; i++)
+    gl_fontMakeDList(face, i, font->list_base, font->textures);
+
+  // We can now free the face and library.
+  FT_Done_Face(face);
+  FT_Done_FreeType(library);
+}
+
+void gl_freeFont(gl_font* font) {
+  glDeleteLists(font->list_base, 128);
+  glDeleteTextures(128, font->textures);
+  free(font->textures);
+}
+
+// ================
+// GLOBAL.
+// ================
+
 // Initialize SDL/OpenGL etc.
 int gl_init(void) {
   int depth, i, supported = 0;
diff --git a/src/opengl.h b/src/opengl.h
index 42e8770..7141a01 100644
--- a/src/opengl.h
+++ b/src/opengl.h
@@ -39,16 +39,28 @@ typedef struct {
   GLuint texture; // The opengl texture itself.
 } gl_texture;
 
+// Font info.
+typedef struct {
+  float h; // Height.
+  GLuint* textures;
+  GLuint list_base;
+} gl_font;
+
+// gl_font loading/freeing.
+void gl_fontInit(gl_font* font, const char* fname, unsigned int h);
+void gl_freeFont(gl_font* font);
+
 // gl_texute loading/freeing.
 gl_texture* gl_loadImage(SDL_Surface* surface); // Frees the surface.
 gl_texture* gl_newImage(const char* path);
 gl_texture* gl_newSprite(const char* path, const int sx, const int sy);
-void gl_free(gl_texture* texture);
+void gl_freeTexture(gl_texture* texture);
 
 // Rendering.
-void gl_blitSprite(gl_texture* sprite, Vec2* pos, const int sx, const int sy);
-void gl_blitStatic(gl_texture* texture, Vec2* pos);
-void gl_bindCamera(Vec2* pos);
+void gl_blitSprite(const gl_texture* sprite, const Vec2* pos, const int sx, const int sy);
+void gl_blitStatic(const gl_texture* texture, const Vec2* pos);
+void gl_bindCamera(const Vec2* pos);
+void gl_print(const gl_font* ft_font, Vec2* pos, const char* fmt, ...);
 
 // Initialize/cleanup.
 int gl_init(void);
diff --git a/src/pilot.h b/src/pilot.h
index 8fb3e0f..8794e1d 100644
--- a/src/pilot.h
+++ b/src/pilot.h
@@ -53,7 +53,7 @@ unsigned int pilot_create(Ship* ship, char* name, const Vec2* vel,
       const Vec2* pos, const int flags);
 
 // Cleanup.
-void pilot_free(void);
+void pilots_free(void);
 
 // Update.
 void pilots_update(FP dt);
diff --git a/src/ship.c b/src/ship.c
index 6475133..056dba7 100644
--- a/src/ship.c
+++ b/src/ship.c
@@ -145,7 +145,7 @@ void ships_free(void) {
   for(i = 0; i < ships; i++) {
     if((ship_stack+i)->name)
       free((ship_stack+i)->name);
-    gl_free((ship_stack+i)->gfx_ship);
+    gl_freeTexture((ship_stack+i)->gfx_ship);
   }
   free(ship_stack);
   ship_stack = NULL;
diff --git a/src/space.c b/src/space.c
index 170ee0c..e7f70ec 100644
--- a/src/space.c
+++ b/src/space.c
@@ -151,6 +151,6 @@ void space_render(FP dt) {
 void space_exit(void) {
   int i;
   for(i = 0; i < STAR_LAYERS; i++)
-    gl_free(starBG[i]);
+    gl_freeTexture(starBG[i]);
 }