diff --git a/flixel/input/gamepad/FlxGamepad.hx b/flixel/input/gamepad/FlxGamepad.hx
index a6dfdd2726..62c342b732 100644
--- a/flixel/input/gamepad/FlxGamepad.hx
+++ b/flixel/input/gamepad/FlxGamepad.hx
@@ -1,5 +1,8 @@
 package flixel.input.gamepad;
 
+import flixel.system.replay.IntegerFloatPair;
+import flixel.system.replay.GamepadRecord;
+import flixel.system.replay.CodeValuePair;
 import flixel.input.FlxInput.FlxInputState;
 import flixel.input.gamepad.FlxGamepadMappedInput;
 import flixel.input.gamepad.lists.FlxGamepadAnalogList;
@@ -120,6 +123,11 @@ class FlxGamepad implements IFlxDestroyable
 	 */
 	public var pointer(default, null):FlxGamepadPointerValueList;
 
+	/**
+	 * Returns axis values from the `axis` list, as if gamepads were turned off.
+	**/
+	public var recording:Bool = false;
+
 	#if FLX_JOYSTICK_API
 	public var hat(default, null):FlxPoint = FlxPoint.get();
 	public var ball(default, null):FlxPoint = FlxPoint.get();
@@ -702,6 +710,15 @@ class FlxGamepad implements IFlxDestroyable
 
 	function getAxisValue(AxisID:Int):Float
 	{
+		if (recording)
+		{
+			if (AxisID < 0 || AxisID >= axis.length)
+			{
+				return 0;
+			}
+			return axis[AxisID];
+		}
+		
 		var axisValue:Float = 0;
 
 		#if FLX_GAMEINPUT_API
@@ -722,6 +739,7 @@ class FlxGamepad implements IFlxDestroyable
 		axisValue = axis[AxisID];
 		#end
 
+
 		if (isAxisForAnalogStick(AxisID))
 		{
 			axisValue = applyAxisFlip(axisValue, AxisID);
@@ -908,6 +926,76 @@ class FlxGamepad implements IFlxDestroyable
 			LabelValuePair.weak("deadZone", deadZone)
 		]);
 	}
+	/**
+	 * If any buttons are not "released",
+	 * this function will return an array indicating
+	 * which buttons are pressed and what state they are in.
+	 *
+	 * @return	An array of button state data. Null if there is no data.
+	 */
+	@:allow(flixel.system.replay.FlxReplay)
+	function record():GamepadRecord
+	{
+		var data:Array<CodeValuePair> = null;
+		
+		for (button in buttons)
+		{
+			if (button == null || button.released)
+			{
+				continue;
+			}
+			
+			if (data == null)
+			{
+				data = new Array<CodeValuePair>();
+			}
+			
+			data.push(new CodeValuePair(button.ID, button.current));
+		}
+		
+		var analogData:Array<IntegerFloatPair> = new Array<IntegerFloatPair>();
+		
+		for (i in 0...axis.length)
+		{
+			var axisValue = getAxisValue(i);
+			if (axisValue != 0)
+				analogData.push(new IntegerFloatPair(i, axisValue));
+
+		}
+		
+		return new GamepadRecord(id, data, analogData);
+	}
+	
+	/**
+	 * Part of the gamepad recording system.
+	 * Takes data about analog and buttons and sets it into array.
+	 *
+	 * @param	Record	Array of data about key states.
+	 */
+	@:allow(flixel.system.replay.FlxReplay)
+	function playback(record:GamepadRecord):Void
+	{
+		var i = 0;
+		final len = record.buttons.length;
+		
+		while (i < len)
+		{
+			final keyRecord = record.buttons[i++];
+			final id = getButton(keyRecord.code);
+			id.current = keyRecord.value;
+		}
+		i = 0;
+		final len = record.analog.length;
+		for (i in 0...axis.length)
+		{
+			axis[i] = 0;
+		}
+		while (i < len)
+		{
+			final keyRecord = record.analog[i++];
+			axis[keyRecord.code] = keyRecord.value;
+		}
+	}
 }
 
 enum FlxGamepadDeadZoneMode
diff --git a/flixel/input/gamepad/FlxGamepadManager.hx b/flixel/input/gamepad/FlxGamepadManager.hx
index 0477304e5c..b73088aa54 100644
--- a/flixel/input/gamepad/FlxGamepadManager.hx
+++ b/flixel/input/gamepad/FlxGamepadManager.hx
@@ -371,6 +371,12 @@ class FlxGamepadManager implements IFlxInputManager
 		return -1;
 	}
 
+	public function addPlaybackGamepad(gamepad:FlxGamepad)
+	{
+		_gamepads[gamepad.id] = gamepad;
+		_activeGamepads.push(gamepad);
+	}
+
 	function addGamepad(Device:GameInputDevice):Void
 	{
 		if (Device == null)
diff --git a/flixel/system/replay/FlxReplay.hx b/flixel/system/replay/FlxReplay.hx
index 74d0f5ba5d..3ada38f233 100644
--- a/flixel/system/replay/FlxReplay.hx
+++ b/flixel/system/replay/FlxReplay.hx
@@ -1,5 +1,6 @@
 package flixel.system.replay;
 
+import flixel.input.gamepad.FlxGamepad;
 import flixel.FlxG;
 import flixel.util.FlxArrayUtil;
 
@@ -165,6 +166,19 @@ class FlxReplay
 			continueFrame = false;
 		#end
 
+		#if FLX_GAMEPAD
+		var gamepadRecords:Array<GamepadRecord> = new Array();
+		for (gamepad in FlxG.gamepads.getActiveGamepads())
+		{
+			var gamepadRecord:GamepadRecord = gamepad.record();
+			if (gamepadRecord != null)
+			{
+				gamepadRecords.push(gamepadRecord);
+				continueFrame = false;
+			}
+		}
+		#end
+
 		if (continueFrame)
 		{
 			frame++;
@@ -178,6 +192,9 @@ class FlxReplay
 		#if FLX_KEYBOARD
 		frameRecorded.keys = keysRecord;
 		#end
+		#if FLX_GAMEPAD
+		frameRecorded.gamepad = gamepadRecords;
+		#end
 
 		_frames[frameCount++] = frameRecorded;
 
@@ -188,6 +205,8 @@ class FlxReplay
 		}
 	}
 
+	var fakeGamepads:Map<Int, FlxGamepad> = [];
+
 	/**
 	 * Get the current frame record data and load it into the input managers.
 	 */
@@ -220,6 +239,28 @@ class FlxReplay
 			FlxG.mouse.playback(fr.mouse);
 		}
 		#end
+		#if FLX_GAMEPAD
+		if (fr.gamepad != null)
+		{
+			for (record in fr.gamepad)
+			{
+				var gamepad = null;
+				if (fakeGamepads.exists(record.gamepadID))
+				{
+					gamepad = fakeGamepads.get(record.gamepadID);
+				}
+				else
+				{
+					gamepad = new FlxGamepad(-record.gamepadID, FlxG.gamepads, FlxGamepadModel.UNKNOWN, null);
+					FlxG.gamepads.addPlaybackGamepad(gamepad);
+					gamepad.recording = true;
+					fakeGamepads.set(record.gamepadID, gamepad);
+				}
+				
+				gamepad.playback(record);
+			}
+		}
+		#end
 	}
 
 	/**
diff --git a/flixel/system/replay/FrameRecord.hx b/flixel/system/replay/FrameRecord.hx
index 438e62ce01..5035eeafe5 100644
--- a/flixel/system/replay/FrameRecord.hx
+++ b/flixel/system/replay/FrameRecord.hx
@@ -20,6 +20,11 @@ class FrameRecord
 	 */
 	public var mouse:MouseRecord;
 
+	/**
+	 * An array containing all the gamepad inputs.
+	 */
+	public var gamepad:Array<GamepadRecord>;
+
 	/**
 	 * Instantiate array new frame record.
 	 */
@@ -28,6 +33,7 @@ class FrameRecord
 		frame = 0;
 		keys = null;
 		mouse = null;
+		gamepad = null;
 	}
 
 	/**
@@ -37,11 +43,12 @@ class FrameRecord
 	 * @param Mouse		Mouse data from the mouse manager.
 	 * @return A reference to this FrameRecord object.
 	 */
-	public function create(Frame:Float, ?Keys:Array<CodeValuePair>, ?Mouse:MouseRecord):FrameRecord
+	public function create(Frame:Float, ?Keys:Array<CodeValuePair>, ?Mouse:MouseRecord, ?Gamepad:Array<GamepadRecord>):FrameRecord
 	{
 		frame = Math.floor(Frame);
 		keys = Keys;
 		mouse = Mouse;
+		gamepad = Gamepad;
 
 		return this;
 	}
@@ -53,6 +60,7 @@ class FrameRecord
 	{
 		keys = null;
 		mouse = null;
+		gamepad = null;
 	}
 
 	/**
@@ -85,6 +93,44 @@ class FrameRecord
 			output += mouse.x + "," + mouse.y + "," + mouse.button + "," + mouse.wheel;
 		}
 
+		if (gamepad != null)
+		{
+			for (record in gamepad)
+			{
+				output += "g";
+				if (record != null)
+				{
+					output += record.gamepadID + ",";
+					var object:CodeValuePair;
+					var i:Int = 0;
+					var l:Int = record.buttons.length;
+					while (i < l)
+					{
+						if (i > 0)
+						{
+							output += ",";
+						}
+						object = record.buttons[i++];
+						output += object.code + ":" + object.value;
+					}
+					output += "/";
+					var object:IntegerFloatPair;
+					var i:Int = 0;
+					var l:Int = record.analog.length;
+					while (i < l)
+					{
+						if (i > 0)
+						{
+							output += ",";
+						}
+						object = record.analog[i++];
+						output += object.code + ":" + object.value;
+					}
+				}
+			}
+		}
+
+
 		return output;
 	}
 
@@ -103,8 +149,10 @@ class FrameRecord
 
 		// split up keyboard and mouse data
 		array = array[1].split("m");
+		var gamepadArray:Array<String> = array[1].split("g");
+
 		var keyData:String = array[0];
-		var mouseData:String = array[1];
+		var mouseData:String = gamepadArray.splice(0, 1)[0];
 
 		// parse keyboard data
 		if (keyData.length > 0)
@@ -140,6 +188,63 @@ class FrameRecord
 			}
 		}
 
+		if (gamepadArray.length > 0)
+		{
+			for (gamepadString in gamepadArray)
+			{
+				array = gamepadString.split(",");
+				var currentGamepad:GamepadRecord = new GamepadRecord(Std.parseInt(array[0]), [], []);
+				
+				var inputsArray:Array<String> = gamepadString.substring(array[0].length + 1).split("/");
+				var buttonsArray:Array<String> = inputsArray[0].split(",");
+				var analogArray:Array<String> = inputsArray[1].split(",");
+				
+				// go through each data pair and enter it into this frame's button state
+				var buttonPair:Array<String>;
+				i = 0;
+				l = inputsArray[0].length;
+				while (i < l)
+				{
+					var pairString = buttonsArray[i++];
+					if (pairString != null)
+					{
+						buttonPair = pairString.split(":");
+						if (buttonPair.length == 2)
+						{
+							if (gamepad == null)
+							{
+								gamepad = new Array<GamepadRecord>();
+							}
+							currentGamepad.buttons.push(new CodeValuePair(Std.parseInt(buttonPair[0]), Std.parseInt(buttonPair[1])));
+						}
+					}
+				}
+				
+				// go through each data pair and enter it into this frame's analog state
+				var analogPair:Array<String>;
+				i = 0;
+				l = inputsArray[0].length;
+				while (i < l)
+				{
+					var pairString = analogArray[i++];
+					if (pairString != null)
+					{
+						analogPair = pairString.split(":");
+						if (analogPair.length == 2)
+						{
+							if (gamepad == null)
+							{
+								gamepad = new Array<GamepadRecord>();
+							}
+							currentGamepad.analog.push(new IntegerFloatPair(Std.parseInt(analogPair[0]), Std.parseFloat(analogPair[1])));
+						}
+					}
+				}
+				
+				this.gamepad.push(currentGamepad);
+			}
+		}
+
 		return this;
 	}
 }
diff --git a/flixel/system/replay/GamepadRecord.hx b/flixel/system/replay/GamepadRecord.hx
new file mode 100644
index 0000000000..18578445da
--- /dev/null
+++ b/flixel/system/replay/GamepadRecord.hx
@@ -0,0 +1,36 @@
+package flixel.system.replay;
+
+import flixel.input.FlxInput.FlxInputState;
+
+/**
+ * A helper class for the frame records, part of the replay/demo/recording system.
+ */
+class GamepadRecord
+{
+	/**
+	 * The ID of the gamepad being recorded.
+	 */
+	public var gamepadID(default, null):Int;
+
+	/**
+	 * An array referring to digital gamepad buttons and it's state.
+	 */
+	public var buttons(default, null):Array<CodeValuePair>;
+	/**
+	 * An array referring to analog gamepad inputs and their state.
+	 */
+	public var analog(default, null):Array<IntegerFloatPair>;
+
+	/**
+	 * Instantiate a new gamepad input record.
+	 *
+	 * @param   GamepadID	The ID of the gamepad being recorded
+	 * @param	Buttons		An array referring to digital gamepad buttons and it's state.
+	 */
+	public function new(gamepadID:Int, buttons:Array<CodeValuePair>, analog:Array<IntegerFloatPair>)
+	{
+		this.gamepadID = gamepadID;
+		this.buttons = buttons;	
+		this.analog = analog;
+	}
+}
diff --git a/flixel/system/replay/IntegerFloatPair.hx b/flixel/system/replay/IntegerFloatPair.hx
new file mode 100644
index 0000000000..6b35c97c24
--- /dev/null
+++ b/flixel/system/replay/IntegerFloatPair.hx
@@ -0,0 +1,13 @@
+package flixel.system.replay;
+
+class IntegerFloatPair
+{
+	public var code:Int;
+	public var value:Float;
+
+	public function new(code:Int, value:Float)
+	{
+		this.code = code;
+		this.value = value;
+	}
+}