diff --git a/SpritePacker/Packer.cs b/SpritePacker/Packer.cs
deleted file mode 100644
index 0745b59..0000000
--- a/SpritePacker/Packer.cs
+++ /dev/null
@@ -1,137 +0,0 @@
-﻿using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Security.Cryptography;
-using System.IO;
-using System.Configuration;
-using System.Drawing.Text;
-
-namespace SpritePacker
-{
-	public class Packer
-	{
-		private List<Sprite> sprites = new List<Sprite>();
-
-		public Packer()
-		{
-			
-		}
-
-		public void Add(Sprite sprite)
-		{
-			sprites.Add(sprite);
-		}
-		public bool Remove(Sprite sprite)
-		{
-			return sprites.Remove(sprite);
-		}
-
-		public void Arrange()
-		{
-			List<Sprite> arrangedSprites = new List<Sprite>();
-			foreach(Sprite cspr in sprites)
-			{
-				Point scanLines = Point.Empty;
-				Point nextScanLines = new Point(int.MaxValue, int.MaxValue);
-				while(true)
-				{
-					if (!cspr.IntersectsWith(arrangedSprites))
-						break;
-
-					// Scan along the X axis
-					cspr.X = 0;
-					cspr.Y = scanLines.Y;
-
-					bool foundPosition = false;
-					while(cspr.X < scanLines.X)
-					{
-						if (!cspr.IntersectsWith(arrangedSprites))
-						{
-							foundPosition = true;
-							break;
-						}
-
-						// Get the edge furthest to the right
-						List<Sprite> problems = cspr.GetIntersectors(arrangedSprites);
-						Sprite rightProblem = problems[0];
-						foreach (Sprite probSpr in problems)
-						{
-							if (probSpr.Right > rightProblem.Right)
-								rightProblem = probSpr;
-							if (probSpr.Top < nextScanLines.Y)
-								nextScanLines.Y = probSpr.Top + 1;
-						}
-
-						// Move up to the position furthest to the right
-						// NOTE: We may need to add one here.
-						cspr.X = rightProblem.Right + 1;
-
-					}
-
-					if (!foundPosition)
-					{
-						// We didn't find anything along the x axis - let's scan the y axis next
-						cspr.X = scanLines.X;
-						cspr.Y = 0;
-						while (cspr.Y < scanLines.Y)
-						{
-							if (!cspr.IntersectsWith(arrangedSprites))
-							{
-								foundPosition = true;
-								break;
-							}
-
-							// Get the edge furthest downwards
-							List<Sprite> problems = cspr.GetIntersectors(arrangedSprites);
-							Sprite downProblem = problems[0];
-							foreach (Sprite probSpr in problems)
-							{
-								if (probSpr.Bottom > downProblem.Bottom)
-									downProblem = probSpr;
-								if (probSpr.Left < nextScanLines.X)
-									nextScanLines.X = probSpr.Left + 1;
-							}
-							// Move up to the position furthest downwards
-							cspr.Y = downProblem.Bottom + 1;
-						}
-					}
-
-					// If we found a new position, then we don't need to move the scan lines up and try again
-					if (foundPosition)
-						break;
-
-					// Make sure that the next scan lines are sane
-					if (nextScanLines.X == int.MaxValue)
-						nextScanLines.X = scanLines.X;
-					if (nextScanLines.Y == int.MaxValue)
-						nextScanLines.Y = scanLines.Y;
-
-					// If the next scan lines and the current scan lines are identical,
-					// then something is very wrong
-					if(nextScanLines.Equals(scanLines))
-						throw new Exception("Failed to find the next set of lines to scan!");
-
-					// Move the scan lines up to the next nearest ones we've found
-					scanLines = nextScanLines;
-					nextScanLines = new Point(int.MaxValue, int.MaxValue);
-				}
-
-				arrangedSprites.Add(cspr);
-			}
-
-			// We don't need to copy the list of arranged sprites across to the main list here
-			// because Sprite is a class and classes are passed by _reference_.
-		}
-
-		public override string ToString()
-		{
-			string result = string.Format("SpritePacker:");
-
-			foreach (Sprite spr in sprites)
-				result += string.Format("\t{0}\n", spr);
-
-			return result;
-		}
-	}
-}
-
diff --git a/SpritePacker/Program.cs b/SpritePacker/Program.cs
index f17a8c1..61da4c2 100644
--- a/SpritePacker/Program.cs
+++ b/SpritePacker/Program.cs
@@ -23,7 +23,9 @@ namespace SpritePacker
 		private static string versionTextFilename = "SpritePacker.Resources.VersionText.txt";
 		private static string commitHashFilename = "SpritePacker.latest-commit-hash.txt";
 
-		private static Packer spritePacker = new Packer();
+		private static SpritePacker spritePacker;
+
+		private static string outputFilename = "spritesheet.png";
 		private static List<string> values = new List<string>();
 
 		public static bool Verbose = false;
@@ -60,11 +62,15 @@ namespace SpritePacker
 			switch(programMode)
 			{
 				case ProgramMode.Normal:
-					if(values.Count == 0)
-					{
+					if (values.Count == 0) {
 						Console.Error.WriteLine("Error: No filenames specified!");
 						return 1;
 					}
+
+					// Set the output filename to be equal to the first filename in the list
+					outputFilename = values[0];
+					values.RemoveAt(0);
+
 					RunNormal();
 					break;
 				case ProgramMode.DisplayHelpText:
@@ -89,9 +95,11 @@ namespace SpritePacker
 
 		public static void RunNormal()
 		{
+			spritePacker = new SpritePacker(Verbose);
 			addSprites(values);
 			spritePacker.Arrange();
 			Console.WriteLine(spritePacker.ToString());
+			spritePacker.Output(outputFilename);
 		}
 
 		private static void addSprites(List<string> filenames)
diff --git a/SpritePacker/Resources/HelpText.txt b/SpritePacker/Resources/HelpText.txt
index 5f7b94b..c8b4d11 100644
--- a/SpritePacker/Resources/HelpText.txt
+++ b/SpritePacker/Resources/HelpText.txt
@@ -1,5 +1,5 @@
 ﻿SpritePacker {version}, by Starbeamrainbowlabs
-Built at {build-date} from git commit {commit-hash}
+Built at {build-date}
 
 Usage:
     mono ./SpritePacker.exe [flags] [filenames]
diff --git a/SpritePacker/Sprite.cs b/SpritePacker/Sprite.cs
index 16bac91..4234d1e 100644
--- a/SpritePacker/Sprite.cs
+++ b/SpritePacker/Sprite.cs
@@ -1,6 +1,7 @@
 ﻿using System;
 using System.Collections.Generic;
 using System.Drawing;
+using System.Runtime.Remoting.Messaging;
 
 namespace SpritePacker
 {
@@ -8,6 +9,7 @@ namespace SpritePacker
 	{
 		private Rectangle area;
 		private string filename;
+		private Image image;
 
 		public Rectangle Area
 		{
@@ -19,7 +21,9 @@ namespace SpritePacker
 			get { return filename; }
 			set { filename = value; }
 		}
+		public Image Image { get { return image; } }
 
+		public Point Location { get { return new Point(X, Y); } }
 		public int X
 		{
 			get { return area.X; }
@@ -45,11 +49,15 @@ namespace SpritePacker
 		public int Left { get { return area.Left; } }
 		public int Right { get { return area.Right; } }
 
+		public int AreaSize { get { return area.Width * area.Height; } }
+
 		public Sprite(string inFilename)
 		{
 			Filename = inFilename;
-			// TODO: Fill in the area automagically based on the given image
-			throw new NotImplementedException("Todo: Fill in the area automagically based on the given image");
+
+			image = Image.FromFile(filename);
+			Width = image.Width;
+			Height = image.Height;
 		}
 
 		public static int GetLargestSize(List<Sprite> sprList)
@@ -68,6 +76,8 @@ namespace SpritePacker
 			List<Sprite> result = new List<Sprite>();
 			foreach(Sprite spr in spriteList)
 			{
+				if (spr == this) // Don't attempt to check for collisions with ourselves
+					continue;
 				if (spr.IntersectsWith(this))
 					result.Add(spr);
 			}
diff --git a/SpritePacker/SpritePacker.cs b/SpritePacker/SpritePacker.cs
new file mode 100644
index 0000000..91e9593
--- /dev/null
+++ b/SpritePacker/SpritePacker.cs
@@ -0,0 +1,226 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Security.Cryptography;
+using System.IO;
+using System.Configuration;
+using System.Drawing.Text;
+using System.Drawing.Imaging;
+
+namespace SpritePacker
+{
+	public class SpritePacker
+	{
+		/// <summary>
+		/// A list of all the sprites added to the sprite packer.
+		/// </summary>
+		private List<Sprite> sprites = new List<Sprite>();
+
+		public bool Verbose { get; private set; }
+
+		public SpritePacker(bool inVerbose = false)
+		{
+			Verbose = inVerbose;
+		}
+
+		/// <summary>
+		/// Adds a sprite to the sprite packer.
+		/// </summary>
+		/// <param name="sprite">Sprite.</param>
+		public void Add(Sprite sprite)
+		{
+			Console.WriteLine("Adding {0}.", sprite);
+			sprites.Add(sprite);
+		}
+		/// <summary>
+		/// Adds a sprite to the sprite packer.
+		/// </summary>
+		/// <param name="sprite">Whether a sprite was actually removed or not.</param>
+		public bool Remove(Sprite sprite)
+		{
+			return sprites.Remove(sprite);
+		}
+		/// <summary>
+		/// Clears the list of sprites added to the current sprite packer.
+		/// </summary>
+		public void Clear()
+		{
+			sprites.Clear();
+		}
+
+		/// <summary>
+		/// Packs all the added sprites in as small a area as possible.
+		/// Note that this operation may potentially be very computationally expensive.
+		/// </summary>
+		public void Arrange()
+		{
+			sortBySize();
+
+			List<Sprite> arrangedSprites = new List<Sprite>();
+			foreach(Sprite cspr in sprites)
+			{
+				if(Verbose) Console.WriteLine("Attempting to place {0}.", cspr);
+				Point scanLines = Point.Empty;
+				Point nextScanLines = new Point(int.MaxValue, int.MaxValue);
+				while(true)
+				{
+					if (!cspr.IntersectsWith(arrangedSprites))
+						break;
+
+					if(Verbose) Console.WriteLine("Scan lines: {0}", scanLines);
+
+					if(Verbose) Console.WriteLine("Scanning X...");
+					// Scan along the X axis
+					cspr.X = 0;
+					cspr.Y = scanLines.Y;
+
+					bool foundPosition = false;
+					while(cspr.X <= scanLines.X)
+					{
+						if(Verbose) Console.Write("Position: {0} ", cspr.X);
+
+						if (!cspr.IntersectsWith(arrangedSprites))
+						{
+							if(Verbose) Console.WriteLine("Found position on the X axis.");
+							foundPosition = true;
+							break;
+						}
+
+						// Get the edge furthest to the right
+						List<Sprite> problems = cspr.GetIntersectors(arrangedSprites);
+						Sprite rightProblem = problems[0];
+						foreach (Sprite probSpr in problems)
+						{
+							if (probSpr.Right > rightProblem.Right)
+								rightProblem = probSpr;
+							// If the current problem's bottom edge is less than the bottom of the next scan line,
+							// move the next scan line up a bit.
+							// Also make sure that the next scan line and the current scan line don't touch or cross.
+							if (probSpr.Bottom < nextScanLines.Y && probSpr.Bottom > scanLines.Y)
+								nextScanLines.Y = probSpr.Bottom + 1;
+						}
+						if(Verbose) Console.WriteLine("Found rightmost problem: {0}", rightProblem);
+
+						// Move up to the position furthest to the right
+						cspr.X = rightProblem.Right + 1;
+					}
+
+					if (!foundPosition)
+					{
+						if(Verbose) Console.WriteLine("Failed to find anything on the X axis. Scanning Y...");
+						// We didn't find anything along the x axis - let's scan the y axis next
+						cspr.X = scanLines.X;
+						cspr.Y = 0;
+						while (cspr.Y <= scanLines.Y)
+						{
+							if(Verbose) Console.Write("Position: {0} ", cspr.Y);
+							if (!cspr.IntersectsWith(arrangedSprites))
+							{
+								if(Verbose) Console.WriteLine("Found position on the Y axis.");
+								foundPosition = true;
+								break;
+							}
+
+							// Get the edge furthest downwards
+							List<Sprite> problems = cspr.GetIntersectors(arrangedSprites);
+							Sprite downProblem = problems[0];
+							foreach (Sprite probSpr in problems)
+							{
+								if (probSpr.Bottom > downProblem.Bottom)
+									downProblem = probSpr;
+								// If the current problem's right edge is further in than the current next scan line,
+								// move the enxt scan line up to meet it.
+								// Also make sure that the next scan line and the current scan line don't touch or cross.
+								if (probSpr.Right < nextScanLines.X && probSpr.Right > scanLines.X)
+									nextScanLines.X = probSpr.Right + 1;
+							}
+							if(Verbose) Console.WriteLine("Found downProblem {0}", downProblem);
+
+							// Move up to the position furthest downwards
+							cspr.Y = downProblem.Bottom + 1;
+						}
+					}
+
+					// If we found a new position, then we don't need to move the scan lines up and try again
+					if (foundPosition)
+						break;
+
+					if(Verbose) Console.WriteLine("Failed to find a position along the current scan lines.");
+					if(Verbose) Console.WriteLine("Next candidate scan lines: {0}", nextScanLines);
+
+					// Make sure that the next scan lines are sane
+					if (nextScanLines.X == int.MaxValue)
+						nextScanLines.X = scanLines.X;
+					if (nextScanLines.Y == int.MaxValue)
+						nextScanLines.Y = scanLines.Y;
+
+					if(Verbose) Console.WriteLine("Actual next scan lines: {0}", nextScanLines);
+
+					// If the next scan lines and the current scan lines are identical,
+					// then something is very wrong
+					if(nextScanLines.Equals(scanLines))
+						throw new Exception("Failed to find the next set of lines to scan!");
+
+					// Move the scan lines up to the next nearest ones we've found
+					scanLines = nextScanLines;
+					nextScanLines = new Point(int.MaxValue, int.MaxValue);
+				}
+
+				arrangedSprites.Add(cspr);
+				
+				if(Verbose) Console.WriteLine("Finished positioning {0}.", cspr);
+			}
+
+			// We don't need to copy the list of arranged sprites across to the main list here
+			// because Sprite is a class and classes are passed by _reference_.
+		}
+
+		public void Output(string outputFilename)
+		{
+			// Calculate the size of the image we are about to output
+			Point imageSize = new Point(0, 0);
+			foreach(Sprite spr in sprites)
+			{
+				if (spr.Bottom > imageSize.Y)
+					imageSize.Y = spr.Bottom;
+				if (spr.Right > imageSize.X)
+					imageSize.X = spr.Right;
+			}
+
+			Bitmap finalImage = new Bitmap(imageSize.X, imageSize.Y, PixelFormat.Format32bppArgb);
+			finalImage.MakeTransparent();
+			Graphics context = Graphics.FromImage(finalImage);
+			foreach(Sprite spr in sprites)
+			{
+				context.DrawImage(spr.Image, spr.Location);
+			}
+			finalImage.Save(outputFilename);
+
+			context.Dispose();
+			finalImage.Dispose();
+		}
+
+		/// <summary>
+		/// Sorts the sprites by size.
+		/// </summary>
+		private void sortBySize()
+		{
+			sprites.Sort((a, b) => -a.AreaSize.CompareTo(b.AreaSize));
+		}
+
+		/// <summary>
+		/// Returns a string that represents the current sprite packer.
+		/// </summary>
+		/// <returns>A string that represents the current sprite packer.</returns>
+		public override string ToString()
+		{
+			string result = string.Format("SpritePacker:") + Environment.NewLine;
+
+			foreach (Sprite spr in sprites)
+				result += string.Format("\t{0}\n", spr);
+
+			return result;
+		}
+	}
+}
+
diff --git a/SpritePacker/SpritePacker.csproj b/SpritePacker/SpritePacker.csproj
index 09ca7a3..e3cbe55 100644
--- a/SpritePacker/SpritePacker.csproj
+++ b/SpritePacker/SpritePacker.csproj
@@ -19,6 +19,7 @@
     <WarningLevel>4</WarningLevel>
     <Externalconsole>true</Externalconsole>
     <PlatformTarget>x86</PlatformTarget>
+    <Commandlineparameters>result.png /tmp/*.png</Commandlineparameters>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
     <DebugType>full</DebugType>
@@ -39,9 +40,9 @@
   <ItemGroup>
     <Compile Include="Program.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="Packer.cs" />
     <Compile Include="Sprite.cs" />
     <Compile Include="Utilities.cs" />
+    <Compile Include="SpritePacker.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ItemGroup>
