diff --git a/src/joystick.c b/src/joystick.c
index 60db0e5..e86e57e 100644
--- a/src/joystick.c
+++ b/src/joystick.c
@@ -4,18 +4,26 @@
  * @brief Handles joystick initialization.
  */
 
-#include <SDL/SDL.h>
 #include <string.h>
+#include <SDL/SDL.h>
+#include <SDL/SDL_joystick.h>
+#if SDL_VERSION_ATLEAST(1,3,0)
+#include "SDL/SDL_haptic.h"
+#endif
 #include "lephisto.h"
 #include "log.h"
 #include "joystick.h"
 
 static SDL_Joystick* joystick = NULL; /**< Current joystick in use. */
+#if SDL_VERSION_ATLEAST(1,3,0)
+static int has_haptick = 0;     /**< Does the player have haptic? */
+SDL_Haptic* haptic = NULL;      /**< Current haptic to use, externed in spfx.c */
+unsigned int haptic_query = 0;  /**< Properties of the haptic device. */
+#endif
 
+static void joystick_initHaptic(void);
 
 /**
- * @fn int joystick_get(char* namjoystick)
- *
  * @brief Get the joystick index by name.
  *    @param namjoystick Looks for given string in the joystick name.
  *    @return The index if found, defaults to 0 if it isn't found.
@@ -32,21 +40,22 @@ int joystick_get(char* namjoystick) {
 }
 
 /**
- * @fn int joystick_use(int indjoystick)
- *
  * @brief Make the game use a joystick by index.
  *    @param indjoystick Index of the joystick to use.
  *    @return 0 on success.
  */
 int joystick_use(int indjoystick) {
+  /* Check to see if it exists. */
   if(indjoystick < 0 || indjoystick >= SDL_NumJoysticks()) {
     WARN("Joystick of index number %d does not exist. Switching to default (0)",
          indjoystick);
     indjoystick = 0;
   }
-  if(joystick)
-    /* Might as well close it if it is open already. */
+  /* Close if already open. */
+  if(joystick != NULL) {
     SDL_JoystickClose(joystick);
+    joystick = NULL;
+  }
   /* Start using the joystick. */
   joystick = SDL_JoystickOpen(indjoystick);
   if(joystick == NULL) {
@@ -55,16 +64,51 @@ int joystick_use(int indjoystick) {
     return -1;
   }
   LOG("Using joystick %d - %s", indjoystick, SDL_JoystickName(indjoystick));
-  DEBUG("\t\tWith %d axes, %d buttons, %d balls, and %d hats\n",
+  DEBUG("   With %d axes, %d buttons, %d balls, and %d hats\n",
         SDL_JoystickNumAxes(joystick), SDL_JoystickNumButtons(joystick),
         SDL_JoystickNumBalls(joystick), SDL_JoystickNumHats(joystick));
 
+  /* Initialize the haptic if possible. */
+  joystick_initHaptic();
+
+  /* For style purposes. */
+  DEBUG();
+
   return 0;
 }
 
 /**
- * @fn int joystick_init(void)
- *
+ * @brief Initializes force feedback for the loaded device.
+ */
+static void joystick_initHaptic(void) {
+#if SDL_VERSION_ATLEAST(1,3,0)
+  if(has_haptic && SDL_JoystickIsHaptic(joystick)) {
+    /* Close haptic if already open. */
+    if(haptic != NULL) {
+      SDL_HapticClose(haptic);
+      haptic = NULL;
+    }
+
+    /* Try to create haptic device. */
+    haptic = SDL_HapticOpenFromJoystick(joystick);
+    if(haptic == NULL) {
+      WARN("Unable to initialize force feedback.");
+      return;
+    }
+
+    /* Check to see what it supports. */
+    haptic_query = SDL_HapticQuery(haptic);
+    if(!(haptic_query & SDL_HAPTIC_SINE)) {
+      SDL_HapticClose(haptic);
+      haptic = NULL;
+      return;
+    }
+    DEBUG("   force feedback enabled");
+  }
+#endif
+}
+
+/**
  * @brief Initializes the joystick subsystem.
  *    @return 0 on success.
  */
@@ -77,6 +121,11 @@ int joystick_init(void) {
     return -1;
   }
 
+#if SDL_VERSION_ATLEAST(1,3,0)
+  if(SDL_InitSubSystem(SDL_INIT_HAPTIC) == 0)
+    has_haptic = 1;
+#endif
+
   /* Figure out how many joysticks there are. */
   numjoysticks = SDL_NumJoysticks();
   LOG("%d joystick%s detected", numjoysticks, (numjoysticks==1)?"":"s");
@@ -90,11 +139,18 @@ int joystick_init(void) {
 }
 
 /**
- * @fn void joystick_exit(void)
- *
  * @brief Exit the joystick subsystem.
  */
 void joystick_exit(void) {
-  SDL_JoystickClose(joystick);
+#if SDL_VERSION_ATLEAST(1,3,0)
+  if(haptic != NULL) {
+    SDL_HapticClose(haptic);
+    haptic = NULL;
+  }
+#endif
+  if(joystick != NULL) {
+    SDL_JoystickClose(joystick);
+    joystick = NULL;
+  }
 }
 
diff --git a/src/spfx.c b/src/spfx.c
index 441d0d9..a1afa08 100644
--- a/src/spfx.c
+++ b/src/spfx.c
@@ -4,8 +4,11 @@
  * @brief Handle the specific effects.
  */
 
-#include <SDL.h>
-
+#include <inttypes.h>
+#include <SDL/SDL.h>
+#if SDL_VERSION_ATLEAST(1,3,0)
+#include "SDL_haptic.h"
+#endif
 #include "lephisto.h"
 #include "log.h"
 #include "pilot.h"
@@ -27,6 +30,13 @@ Vec2 shake_pos = { .x = 0., .y = 0. };    /**< Current shake pos. Used in nebula
 static Vec2 shake_vel = { .x = 0., .y = 0. }; /**< Current shake vel. */
 static int shake_off = 1;                 /**< 1 if shake is not active. */
 
+#if SDL_VERSION_ATLEAST(1,3,0)
+extern SDL_Haptic* haptic;                    /**< From joystick.c */
+extern unsigned int haptic_query;             /**< From joystick.c */
+static int haptic_rumble = -1;                /**< Haptic rumble effect ID. */
+static SDL_HapticEffect haptic_rumbleEffect;  /**< Haptic rumble effect. */
+#endif
+
 /**
  * @struct SPFX_Base
  *
@@ -67,14 +77,16 @@ static SPFX* spfx_stack_back   = NULL;    /**< Back special effect layer. */
 static int   spfx_nstack_back  = 0;       /**< Number of special effects in the back. */
 static int   spfx_mstack_back  = 0;       /**< Memory allocated for special effects in back. */
 
+/* General. */
 static int spfx_base_load(char* name, int ttl, int anim, char* gfx, int sx, int sy);
 static void spfx_base_free(SPFX_Base* effect);
 static void spfx_destroy(SPFX* layer, int* nlayer, int spfx);
 static void spfx_update_layer(SPFX* layer, int* nlayer, const double dt);
+/* Haptic. */
+static int spfx_hapticInit(void);
+static void spfx_hapticRumble(void);
 
 /**
- * @fn static int spfx_base_load(char* name, int ttl, int anim, char* gfx, int sx, int sy)
- *
  * @brief Load an SPFX_Base into the stack based on some params.
  *    @param name Name of the spfx.
  *    @param ttl Time to live of the spfx.
@@ -102,8 +114,6 @@ static int spfx_base_load(char* name, int ttl, int anim, char* gfx, int sx, int
 }
 
 /**
- * @fn static void spfx_base_free(SPFX_Base* effect)
- *
  * @brief Free an SPFX_Base.
  *    @param effect SPFX_Base to free.
  */
@@ -113,8 +123,6 @@ static void spfx_base_free(SPFX_Base* effect) {
 }
 
 /**
- * @fn int spfx_get(char* name)
- *
  * @brief Get the id of an spfx based on name.
  *    @param name Name to match.
  *    @return ID of the special effect or -1 on error.
@@ -129,8 +137,6 @@ int spfx_get(char* name) {
 }
 
 /**
- * @fn int spfx_load(void)
- *
  * @brief Load the spfx stack.
  *    @return 0 on success.
  * 
@@ -152,12 +158,13 @@ int spfx_load(void) {
   /* Plasma hits. */
   spfx_base_load("PlaS", 400, 400, "plas.png", 6, 5);
   spfx_base_load("PlaM", 450, 450, "plam.png", 6, 5);
+
+  spfx_hapticInit();
+
   return 0;
 }
 
 /**
- * @fn void spfx_free(void)
- *
  * @brief Free the spfx stack.
  */
 void spfx_free(void) {
@@ -180,11 +187,6 @@ void spfx_free(void) {
 }
 
 /**
- * @fn void spfx_add(int effect,
- *             const double px, const double py,
- *             const double vx, const double vy,
- *             const int layer) {
- *
  * @brief Create a new special effect.
  *    @param effect Base effect identifier to use.
  *    @param px X position of the effect.
@@ -242,8 +244,6 @@ void spfx_add(int effect,
 }
 
 /**
- * @fn void spfx_clear(void)
- *
  * @brief Clear all the currently running effects.
  */
 void spfx_clear(void) {
@@ -264,8 +264,6 @@ void spfx_clear(void) {
 }
 
 /**
- * @fn static void spfx_destroy(SPFX* layer, int* nlayer, int spfx)
- *
  * @brief Destroys an active spfx.
  *    @param layer Layer the spfx is on.
  *    @param nlayer Pointer to the number of elements in the layer.
@@ -277,8 +275,6 @@ static void spfx_destroy(SPFX* layer, int* nlayer, int spfx) {
 }
 
 /**
- * @fn void spfx_update(const double dt)
- *
  * @brief Update all the spfx.
  *    @param dt Current delta tick.
  */
@@ -288,8 +284,6 @@ void spfx_update(const double dt) {
 }
 
 /**
- * @fn static void spfx_update_layer(SPFX* layer, int* nlayer, const double dt)
- *
  * @brief Update an individual spfx.
  *    @param layer Layer the spfx is on.
  *    @param nlayer Pointer to the assosiated nlayer.
@@ -313,8 +307,6 @@ static void spfx_update_layer(SPFX* layer, int* nlayer, const double dt) {
 }
 
 /**
- * @fn void spfx_start(double dt)
- *
  * @brief Prepare the rendering for the special effects.
  *
  * Should be called at the beginning of the rendering loop.
@@ -360,24 +352,78 @@ void spfx_start(const double dt) {
 }
 
 /**
- * @fn void spfx_shake(double mod)
- *
  * @brief Increases the current rumble level.
  *
  * Rumble will decay over time.
  *    @param mod Modifier to increase levely by.
  */
 void spfx_shake(double mod) {
+  /* Add the modifier. */
   shake_rad += mod;
   if(shake_rad > SHAKE_MAX) shake_rad = SHAKE_MAX;
-  shake_off = 0;
-
   vect_pset(&shake_vel, SHAKE_VEL_MOD*shake_rad, RNGF() * 2. * M_PI);
+
+  /* Rumble if it wasn't rumbling before. */
+  if(shake_off == 1)
+    spfx_hapticRumble();
+
+  /* Notify that rumble is active. */
+  shake_off = 0;
+}
+
+/**
+ * @brief Initializes the rumble effect.
+ *    @return 0 on success.
+ */
+static int spfx_hapticInit(void) {
+#if SDL_VERSION_ATLEAST(1,3,0)
+  SDL_HapticEffect* efx;
+
+  efx = &haptic_rumbleEffect;
+  memset(efx, 0, sizeof(SDL_HapticEffect));
+  efx->type = SDL_HAPTIC_SINE;
+  efx->periodic.direction.type  = SDL_HAPTIC_POLAR;
+  efx->periodic.length          = 1000;
+  efx->periodic.period          = 200;
+  efx->periodic.magnitude       = 0x4000;
+  efx->periodic.fade_length     = 1000;
+  efx->periodic.fade_level      = 0;
+
+  haptic_rumble = SDL_HapticNewEffect(haptic, efx);
+  if(haptic_rumble < 0) {
+    WARN("Unable to upload haptic effect: %s.", SDL_GetError());
+    return -1;
+  }
+#endif
+  return 0;
+}
+
+/**
+ * @brief Run a rumble effect.
+ */
+static void spfx_hapticRumble(void) {
+#if SDL_VERSION_ATLEAST(1,3,0)
+  SDL_HapticEffect* efx;
+
+  if(haptic_rumble >= 0) {
+    /* Stop the effect if it was playing. */
+    SDL_HapticStopEffect(haptic, haptic_rumble);
+
+    /* Update the effect. */
+    efx = &haptic_rumbleEffect;
+    efx->periodic.length = (uint32_t)(1000.*shake_rad / SHAKE_DECAY);
+    if(SDL_HapticUpdateEffect(haptic, haptic_rumble, &haptic_rumbleEffect) < 0) {
+      WARN("Failed to update effect: %s.", SDL_GetError());
+      return;
+    }
+
+    /* Run the new effect. */
+    SDL_HapticRunEffect(haptic, haptic_rumble, 1);
+  }
+#endif
 }
 
 /**
- * @fn void spfx_cinematic(void)
- *
  * @brief Set the cinematic mode.
  *
  * Should be run at the end of the render loop if needed.
@@ -403,8 +449,6 @@ void spfx_cinematic(void) {
 }
 
 /**
- * @fn void spfx_render(const int layer)
- * 
  * @brief Render the entire spfx layer.
  *    @param layer Layer to render.
  */