Index: code/freespace2/freespace.cpp
===================================================================
--- code/freespace2/freespace.cpp	(revision 8233)
+++ code/freespace2/freespace.cpp	(working copy)
@@ -4197,49 +4197,38 @@
 void game_reset_shade_frame()
 {
 	Fade_type = FI_NONE;
-	Fade_delta_time = 1.0f;
 	gr_create_shader(&Viewer_shader, 0, 0, 0, 0);
 }
 
 void game_shade_frame(float frametime)
 {
-	int alpha = 0;
-
 	// only do frame shade if we are actually in a game play state
 	if ( !game_actually_playing() ) {
 		return;
 	}
 
-	if (Fade_type != FI_NONE)
-	{
-		if ( (Viewer_shader.c == 0) && (Fade_type != FI_FADEOUT) ) {
-			return;
-		}
+	if (Fade_type != FI_NONE) {
+		Assert(Fade_start_timestamp > 0);
+		Assert(Fade_end_timestamp > 0);
+		Assert(Fade_end_timestamp > Fade_start_timestamp);
 
-		alpha = Viewer_shader.c;
+		int startAlpha = 0;
+		int endAlpha = 0;
 
 		// Fade in or out if necessary
 		if (Fade_type == FI_FADEOUT) {
-			alpha += fl2i(frametime * (255.0f / Fade_delta_time) + 0.5f);
+			endAlpha = 255;
 		} else if (Fade_type == FI_FADEIN) {
-			alpha -= fl2i(frametime * (255.0f / Fade_delta_time) + 0.5f);
+			startAlpha = 255;
 		}
 
-		// Limit and set fade type if done
-		if (alpha < 0) {
-			alpha = 0;
+		int duration = (Fade_end_timestamp - Fade_start_timestamp);
+		int elapsed = (timestamp() - Fade_start_timestamp);
 
-			if (Fade_type == FI_FADEIN) {
-				Fade_type = FI_NONE;
-			}
-		}
+		int alpha = fl2i( (float)startAlpha + (((float)endAlpha - (float)startAlpha) / (float)duration) * (float)elapsed );
 
-		if (alpha > 255) {
-			alpha = 255;
-
-			if (Fade_type == FI_FADEOUT) {
-				Fade_type = FI_NONE;
-			}
+		if (alpha == endAlpha) {
+			Fade_type = FI_NONE;
 		}
 
 		Viewer_shader.c = (ubyte)alpha;
Index: code/globalincs/systemvars.cpp
===================================================================
--- code/globalincs/systemvars.cpp	(revision 8233)
+++ code/globalincs/systemvars.cpp	(working copy)
@@ -36,8 +36,9 @@
 
 //FADEIN STUFF
 shader Viewer_shader;
-int Fade_type = FI_NONE;
-float Fade_delta_time = 1.0f;
+FadeType Fade_type = FI_NONE;
+int Fade_start_timestamp = 0;
+int Fade_end_timestamp = 0;
 
 // The detail level.  Anything below zero draws simple models earlier than it
 // should.   Anything above zero draws higher detail models longer than it should.
Index: code/globalincs/systemvars.h
===================================================================
--- code/globalincs/systemvars.h	(revision 8233)
+++ code/globalincs/systemvars.h	(working copy)
@@ -58,13 +58,17 @@
 //-----Fadein stuff
 struct shader;
 extern shader Viewer_shader;
-#define FI_NONE					0
-#define FI_FADEIN				1
-#define FI_FADEOUT				2
-extern float Fade_delta_time;
-extern int Fade_type;
 
+enum FadeType {
+	FI_NONE,
+	FI_FADEIN,
+	FI_FADEOUT
+};
+extern FadeType Fade_type;
+extern int Fade_start_timestamp;
+extern int Fade_end_timestamp;
 
+
 typedef struct vei {
 	angles_t	angles;			//	Angles defining viewer location.
 	float		distance;		//	Distance from which to view, plus 2x radius.
Index: code/parse/sexp.cpp
===================================================================
--- code/parse/sexp.cpp	(revision 8233)
+++ code/parse/sexp.cpp	(working copy)
@@ -18324,52 +18324,49 @@
 
 void sexp_fade_in(int n)
 {
-	float delta_time = 0.0f;
+	int duration = 0;
 
 	if(n != -1)
-		delta_time = eval_num(n)/1000.0f;
+		duration = eval_num(n);
 
-	if(delta_time > 0.0f)
-	{
-		Fade_delta_time = delta_time;
+	if (duration > 0) {
+		Fade_start_timestamp = timestamp();
+		Fade_end_timestamp = timestamp(duration);
 		Fade_type = FI_FADEIN;
-	}
-	else
-	{
+	} else {
 		Fade_type = FI_NONE;
 		gr_create_shader(&Viewer_shader, 0, 0, 0, 0);
 	}
 
 	// multiplayer callback
 	multi_start_callback();
-	multi_send_float(delta_time);
+	multi_send_int(duration);
 	multi_end_callback();
 }
 
 void multi_sexp_fade_in()
 {
-	float delta_time = 0.0f;
+	int duration = 0;
 
-	multi_get_float(delta_time);
+	multi_get_int(duration);
 
-	if(delta_time > 0.0f) {
-		Fade_delta_time = delta_time;
+	if (duration > 0) {
+		Fade_start_timestamp = timestamp();
+		Fade_end_timestamp = timestamp(duration);
 		Fade_type = FI_FADEIN;
-	}
-	else {
+	} else {
 		Fade_type = FI_NONE;
 		gr_create_shader(&Viewer_shader, 0, 0, 0, 0);
 	}
 }
 
-void sexp_fade_out(float delta_time, int fade_type) 
+void sexp_fade_out(int duration, int fadeColor)
 {
 	ubyte R = 0;
 	ubyte G = 0;
 	ubyte B = 0;
 
-	switch(fade_type)
-	{
+	switch(fadeColor) {
 		//White out
 		case 1:
 			gr_create_shader(&Viewer_shader, 255, 255, 255, Viewer_shader.c);
@@ -18381,18 +18378,18 @@
 		//Black out
 		default:
 			gr_create_shader(&Viewer_shader, 0, 0, 0, Viewer_shader.c);
+			break;
 	}
 
 	R = Viewer_shader.r;
 	G = Viewer_shader.g;
 	B = Viewer_shader.b;
 
-	if(delta_time > 0.0f) {
+	if (duration > 0) {
+		Fade_start_timestamp = timestamp();
+		Fade_end_timestamp = timestamp(duration);
 		Fade_type = FI_FADEOUT;
-		Fade_delta_time = delta_time;
-	}
-	else
-	{
+	} else {
 		Fade_type = FI_NONE;
 		gr_create_shader(&Viewer_shader, R, G, B, 255);
 	}
@@ -18400,40 +18397,38 @@
 
 void sexp_fade_out(int n)
 {
-	float delta_time = 0.0f;
-	int fade_type = 0;
+	int duration = 0;
+	int fadeColor = 0;
 
-	if(n != -1)
-	{
-		delta_time = eval_num(n)/1000.0f;
+	if (n != -1) {
+		duration = eval_num(n);
 
 		n = CDR(n);
-		if(n != -1)
-		{
-			fade_type = eval_num(n);			
+		if (n != -1) {
+			fadeColor = eval_num(n);
 		}
 	}
 
-	sexp_fade_out(delta_time, fade_type);
+	sexp_fade_out(duration, fadeColor);
 
 	multi_start_callback();
-	multi_send_float(delta_time);
-	multi_send_int(fade_type);
+	multi_send_int(duration);
+	multi_send_int(fadeColor);
 	multi_end_callback();
 }
 
 void multi_sexp_fade_out()
 {
-	float delta_time = 0.0f;
-	int fade_type;
+	int duration = 0;
+	int fadeColor = 0;
 
-	multi_get_float(delta_time);
-	if (!multi_get_int(fade_type)){
+	multi_get_int(duration);
+	if (!multi_get_int(fadeColor)){
 		Int3();	// misformed packet
 		return;
 	}
 
-	sexp_fade_out(delta_time, fade_type);
+	sexp_fade_out(duration, fadeColor);
 }
 
 camera* sexp_get_set_camera(bool reset = false)
