diff --git a/src/p_spec.c b/src/p_spec.c
index f6a0ce60643037e7eb5ab113d714555103cf11c2..04b333628e40d93f581a09ed306c0b521a622716 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -7408,6 +7408,21 @@ static void Add_Scroller(INT32 type, fixed_t dx, fixed_t dy, INT32 control, INT3
 		s->last_height = sectors[control].floorheight + sectors[control].ceilingheight;
 	s->affectee = affectee;
 	P_AddThinker(THINK_MAIN, &s->thinker);
+
+	// interpolation
+	switch (type)
+	{
+		case sc_side:
+			break;
+		case sc_floor:
+			R_CreateInterpolator_SectorScroll(&s->thinker, &sectors[affectee], false);
+			break;
+		case sc_ceiling:
+			R_CreateInterpolator_SectorScroll(&s->thinker, &sectors[affectee], true);
+			break;
+		default:
+			break;
+	}
 }
 
 /** Initializes the scrollers.
diff --git a/src/r_fps.c b/src/r_fps.c
index b69531574c64ac14a6237fa3d5809b0d3fe06b4d..b672a6f0f7d61a688a805c71dbcc23eb77b8e796 100644
--- a/src/r_fps.c
+++ b/src/r_fps.c
@@ -259,6 +259,23 @@ void R_CreateInterpolator_SectorPlane(thinker_t *thinker, sector_t *sector, bool
 	}
 }
 
+void R_CreateInterpolator_SectorScroll(thinker_t *thinker, sector_t *sector, boolean ceiling)
+{
+	levelinterpolator_t *interp = CreateInterpolator(LVLINTERP_SectorScroll, thinker);
+	interp->sectorscroll.sector = sector;
+	interp->sectorscroll.ceiling = ceiling;
+	if (ceiling)
+	{
+		interp->sectorscroll.oldxoffs = interp->sectorscroll.bakxoffs = sector->ceiling_xoffs;
+		interp->sectorscroll.oldyoffs = interp->sectorscroll.bakyoffs = sector->ceiling_yoffs;
+	}
+	else
+	{
+		interp->sectorscroll.oldxoffs = interp->sectorscroll.bakxoffs = sector->floor_xoffs;
+		interp->sectorscroll.oldyoffs = interp->sectorscroll.bakyoffs = sector->floor_yoffs;
+	}
+}
+
 void R_InitializeLevelInterpolators(void)
 {
 	levelinterpolators_len = 0;
@@ -274,6 +291,12 @@ static void UpdateLevelInterpolatorState(levelinterpolator_t *interp)
 		interp->sectorplane.oldheight = interp->sectorplane.bakheight;
 		interp->sectorplane.bakheight = interp->sectorplane.ceiling ? interp->sectorplane.sector->ceilingheight : interp->sectorplane.sector->floorheight;
 		break;
+	case LVLINTERP_SectorScroll:
+		interp->sectorscroll.oldxoffs = interp->sectorscroll.bakxoffs;
+		interp->sectorscroll.bakxoffs = interp->sectorscroll.ceiling ? interp->sectorscroll.sector->ceiling_xoffs : interp->sectorscroll.sector->floor_xoffs;
+		interp->sectorscroll.oldyoffs = interp->sectorscroll.bakyoffs;
+		interp->sectorscroll.bakyoffs = interp->sectorscroll.ceiling ? interp->sectorscroll.sector->ceiling_yoffs : interp->sectorscroll.sector->floor_yoffs;
+		break;
 	}
 }
 
@@ -326,6 +349,19 @@ void R_ApplyLevelInterpolators(fixed_t frac)
 			{
 				interp->sectorplane.sector->floorheight = R_LerpFixed(interp->sectorplane.oldheight, interp->sectorplane.bakheight, frac);
 			}
+			break;
+		case LVLINTERP_SectorScroll:
+			if (interp->sectorscroll.ceiling)
+			{
+				interp->sectorscroll.sector->ceiling_xoffs = R_LerpFixed(interp->sectorscroll.oldxoffs, interp->sectorscroll.bakxoffs, frac);
+				interp->sectorscroll.sector->ceiling_yoffs = R_LerpFixed(interp->sectorscroll.oldyoffs, interp->sectorscroll.bakyoffs, frac);
+			}
+			else
+			{
+				interp->sectorscroll.sector->floor_xoffs = R_LerpFixed(interp->sectorscroll.oldxoffs, interp->sectorscroll.bakxoffs, frac);
+				interp->sectorscroll.sector->floor_yoffs = R_LerpFixed(interp->sectorscroll.oldyoffs, interp->sectorscroll.bakyoffs, frac);
+			}
+			break;
 		}
 	}
 }
@@ -349,6 +385,19 @@ void R_RestoreLevelInterpolators(void)
 			{
 				interp->sectorplane.sector->floorheight = interp->sectorplane.bakheight;
 			}
+			break;
+		case LVLINTERP_SectorScroll:
+			if (interp->sectorscroll.ceiling)
+			{
+				interp->sectorscroll.sector->ceiling_xoffs = interp->sectorscroll.bakxoffs;
+				interp->sectorscroll.sector->ceiling_yoffs = interp->sectorscroll.bakyoffs;
+			}
+			else
+			{
+				interp->sectorscroll.sector->floor_xoffs = interp->sectorscroll.bakxoffs;
+				interp->sectorscroll.sector->floor_yoffs = interp->sectorscroll.bakyoffs;
+			}
+			break;
 		}
 	}
 }
diff --git a/src/r_fps.h b/src/r_fps.h
index df506fb568b5058e002050789bc4deedf81be263..cc240ffc8cc184cc41b9fc23124705ce526be530 100644
--- a/src/r_fps.h
+++ b/src/r_fps.h
@@ -56,6 +56,7 @@ typedef struct {
 // The union tag for levelinterpolator_t
 typedef enum {
 	LVLINTERP_SectorPlane,
+	LVLINTERP_SectorScroll,
 } levelinterpolator_type_e;
 
 // Tagged union of a level interpolator
@@ -69,6 +70,11 @@ typedef struct levelinterpolator_s {
 			fixed_t bakheight;
 			boolean ceiling;
 		} sectorplane;
+		struct {
+			sector_t *sector;
+			fixed_t oldxoffs, oldyoffs, bakxoffs, bakyoffs;
+			boolean ceiling;
+		} sectorscroll;
 	};
 } levelinterpolator_t;
 
@@ -86,6 +92,7 @@ void R_InterpolateMobjState(mobj_t *mobj, fixed_t frac, interpmobjstate_t *out);
 void R_InterpolatePrecipMobjState(precipmobj_t *mobj, fixed_t frac, interpmobjstate_t *out);
 
 void R_CreateInterpolator_SectorPlane(thinker_t *thinker, sector_t *sector, boolean ceiling);
+void R_CreateInterpolator_SectorScroll(thinker_t *thinker, sector_t *sector, boolean ceiling);
 
 // Initialize level interpolators after a level change
 void R_InitializeLevelInterpolators(void);