diff --git a/indra/llcommon/lltracerecording.cpp b/indra/llcommon/lltracerecording.cpp
index c72a64d086658f8ab032d0570c76c9979b5b7977..5ce1b337fec45f6062c0919abbefa58387a2d882 100644
--- a/indra/llcommon/lltracerecording.cpp
+++ b/indra/llcommon/lltracerecording.cpp
@@ -858,7 +858,6 @@ F64 PeriodicRecording::getPeriodMean( const StatType<EventAccumulator>& stat, S3
 			: NaN;
 }
 
-
 F64 PeriodicRecording::getPeriodStandardDeviation( const StatType<EventAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
 {
     LL_PROFILE_ZONE_SCOPED;
@@ -952,6 +951,32 @@ F64 PeriodicRecording::getPeriodMean( const StatType<SampleAccumulator>& stat, S
 			: NaN;
 }
 
+F64 PeriodicRecording::getPeriodMedian( const StatType<SampleAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
+{
+    LL_PROFILE_ZONE_SCOPED;
+	num_periods = llmin(num_periods, getNumRecordedPeriods());
+
+	std::vector<F64> buf;
+	for (S32 i = 1; i <= num_periods; i++)
+	{
+		Recording& recording = getPrevRecording(i);
+		if (recording.getDuration() > (F32Seconds)0.f)
+		{
+			if (recording.hasValue(stat))
+			{
+				buf.push_back(recording.getMean(stat));
+			}
+		}
+	}
+	if (buf.size()==0)
+	{
+		return 0.0f;
+	}
+	std::sort(buf.begin(), buf.end());
+
+	return F64((buf.size() % 2 == 0) ? (buf[buf.size() / 2 - 1] + buf[buf.size() / 2]) / 2 : buf[buf.size() / 2]);
+}
+
 F64 PeriodicRecording::getPeriodStandardDeviation( const StatType<SampleAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
 {
     LL_PROFILE_ZONE_SCOPED;
diff --git a/indra/llcommon/lltracerecording.h b/indra/llcommon/lltracerecording.h
index 6715104613d6c7a7414a450aa2bcba280042dd3c..1f3d37336a06580fb09e2c01183a048331d064cf 100644
--- a/indra/llcommon/lltracerecording.h
+++ b/indra/llcommon/lltracerecording.h
@@ -599,6 +599,35 @@ namespace LLTrace
 			return typename RelatedTypes<T>::fractional_t(getPeriodMeanPerSec(static_cast<const StatType<CountAccumulator>&>(stat), num_periods));
 		}
 
+        F64 getPeriodMedian( const StatType<SampleAccumulator>& stat, S32 num_periods = S32_MAX);
+
+        template <typename T>
+        typename RelatedTypes<typename T::value_t>::fractional_t getPeriodMedianPerSec(const StatType<T>& stat, S32 num_periods = S32_MAX)
+        {
+            LL_PROFILE_ZONE_SCOPED;
+            num_periods = llmin(num_periods, getNumRecordedPeriods());
+
+            std::vector <typename RelatedTypes<typename T::value_t>::fractional_t> buf;
+            for (S32 i = 1; i <= num_periods; i++)
+            {
+                Recording& recording = getPrevRecording(i);
+                if (recording.getDuration() > (F32Seconds)0.f)
+                {
+                    buf.push_back(recording.getPerSec(stat));
+                }
+            }
+            std::sort(buf.begin(), buf.end());
+
+            return typename RelatedTypes<T>::fractional_t((buf.size() % 2 == 0) ? (buf[buf.size() / 2 - 1] + buf[buf.size() / 2]) / 2 : buf[buf.size() / 2]);
+        }
+
+        template<typename T>
+        typename RelatedTypes<T>::fractional_t getPeriodMedianPerSec(const CountStatHandle<T>& stat, S32 num_periods = S32_MAX)
+        {
+            LL_PROFILE_ZONE_SCOPED;
+            return typename RelatedTypes<T>::fractional_t(getPeriodMedianPerSec(static_cast<const StatType<CountAccumulator>&>(stat), num_periods));
+        }
+
 		//
 		// PERIODIC STANDARD DEVIATION
 		//
diff --git a/indra/llui/llstatbar.cpp b/indra/llui/llstatbar.cpp
index 8adcd664df23daed19192014c604d309f47b6814..244910095205b9f6092d71e1ce313302206ebd4a 100644
--- a/indra/llui/llstatbar.cpp
+++ b/indra/llui/llstatbar.cpp
@@ -160,6 +160,7 @@ LLStatBar::Params::Params()
 	tick_spacing("tick_spacing", 0.f),
 	decimal_digits("decimal_digits", 3),
 	show_bar("show_bar", false),
+	show_median("show_median", false),
 	show_history("show_history", false),
 	scale_range("scale_range", true),
 	num_frames("num_frames", 200),
@@ -186,6 +187,7 @@ LLStatBar::LLStatBar(const Params& p)
 	mNumShortHistoryFrames(p.num_frames_short),
 	mMaxHeight(p.max_height),
 	mDisplayBar(p.show_bar),
+	mShowMedian(p.show_median),
 	mDisplayHistory(p.show_history),
 	mOrientation(p.orientation),
 	mAutoScaleMax(!p.bar_max.isProvided()),
@@ -318,7 +320,14 @@ void LLStatBar::draw()
 			min           = frame_recording.getPeriodMinPerSec(count_stat, num_frames);
 			max           = frame_recording.getPeriodMaxPerSec(count_stat, num_frames);
 			mean          = frame_recording.getPeriodMeanPerSec(count_stat, num_frames);
-			display_value = mean;
+			if (mShowMedian)
+			{
+				display_value = frame_recording.getPeriodMedianPerSec(count_stat, num_frames);
+			}
+			else
+			{
+				display_value = mean;
+			}
 		}
 		break;
 	case STAT_EVENT:
@@ -344,7 +353,11 @@ void LLStatBar::draw()
 			mean              = frame_recording.getPeriodMean(sample_stat, num_frames);
 			num_rapid_changes = calc_num_rapid_changes(frame_recording, sample_stat, RAPID_CHANGE_WINDOW);
 
-			if (num_rapid_changes / RAPID_CHANGE_WINDOW.value() > MAX_RAPID_CHANGES_PER_SEC)
+			if (mShowMedian)
+			{
+				display_value = frame_recording.getPeriodMedian(sample_stat, num_frames);
+			}
+			else if (num_rapid_changes / RAPID_CHANGE_WINDOW.value() > MAX_RAPID_CHANGES_PER_SEC)
 			{
 				display_value = mean;
 			}
diff --git a/indra/llui/llstatbar.h b/indra/llui/llstatbar.h
index 1ff4c67fc5100b82a157b7e4b5e50e00d9753fb8..6b481ca68f78d2d1b1e8bd8de0dc95b7de35acff 100644
--- a/indra/llui/llstatbar.h
+++ b/indra/llui/llstatbar.h
@@ -44,9 +44,10 @@ class LLStatBar : public LLView
 								bar_max,
 								tick_spacing;
 
-		Optional<bool>			show_bar,
+		Optional<bool> 			show_bar,
 								show_history,
-								scale_range;
+								scale_range,
+								show_median; // default is mean
 
 		Optional<S32>			decimal_digits,
 								num_frames,
@@ -112,6 +113,7 @@ class LLStatBar : public LLView
 
 	bool         mDisplayBar,			// Display the bar graph.
 				 mDisplayHistory,
+				 mShowMedian,
 				 mAutoScaleMax,
 				 mAutoScaleMin;
 };
diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp
index 314c1a1f1eca236ed7ec069cd1a49b41ff55ea73..ac8a657fb2a92e0acc6e78cfebf8098f5ac908de 100644
--- a/indra/newview/llviewerstats.cpp
+++ b/indra/newview/llviewerstats.cpp
@@ -183,8 +183,9 @@ SimMeasurement<F64Kilobytes >	SIM_UNACKED_BYTES("simtotalunackedbytes", "", LL_S
 SimMeasurement<F64Megabytes >	SIM_PHYSICS_MEM("physicsmemoryallocated", "", LL_SIM_STAT_SIMPHYSICSMEMORY);
 
 LLTrace::SampleStatHandle<F64Milliseconds >	FRAMETIME_JITTER("frametimejitter", "Average delta between successive frame times"),
-																FRAMETIME_SLEW("frametimeslew", "Average delta between frame time and mean"),
-																SIM_PING("simpingstat");
+											FRAMETIME_SLEW("frametimeslew", "Average delta between frame time and mean"),
+											FRAMETIME("frametime", "Measured frame time"),
+											SIM_PING("simpingstat");
 
 LLTrace::EventStatHandle<LLUnit<F64, LLUnits::Meters> > AGENT_POSITION_SNAP("agentpositionsnap", "agent position corrections");
 
@@ -261,8 +262,12 @@ void LLViewerStats::updateFrameStats(const F64Seconds time_diff)
 		// new "stutter" meter
 		add(LLStatViewer::FRAMETIME_DOUBLED, time_diff >= 2.0 * mLastTimeDiff ? 1 : 0);
 
+		sample(LLStatViewer::FRAMETIME, time_diff);
+
 		// old stats that were never really used
-		sample(LLStatViewer::FRAMETIME_JITTER, F64Milliseconds (mLastTimeDiff - time_diff));
+		F64Seconds jit = (F64Seconds) std::fabs((mLastTimeDiff - time_diff));
+		LL_INFOS() << "times " << mLastTimeDiff << ", " << time_diff << " jit " << jit << LL_ENDL;
+		sample(LLStatViewer::FRAMETIME_JITTER, jit);
 			
 		F32Seconds average_frametime = gRenderStartTime.getElapsedTimeF32() / (F32)gFrameCount;
 		sample(LLStatViewer::FRAMETIME_SLEW, F64Milliseconds (average_frametime - time_diff));
diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h
index 04870e0c26f3b0b6f53879108e7c65e797051ca9..ac8eccc0ca716b6e8d9e3b2a874c07c3c244e968 100644
--- a/indra/newview/llviewerstats.h
+++ b/indra/newview/llviewerstats.h
@@ -218,8 +218,8 @@ extern SimMeasurement<F64Megabytes >	SIM_PHYSICS_MEM;
 
 
 extern LLTrace::SampleStatHandle<F64Milliseconds >	FRAMETIME_JITTER,
-																		FRAMETIME_SLEW,
-																		SIM_PING;
+													FRAMETIME_SLEW,
+													SIM_PING;
 
 extern LLTrace::EventStatHandle<LLUnit<F64, LLUnits::Meters> > AGENT_POSITION_SNAP;
 
diff --git a/indra/newview/skins/default/xui/en/floater_stats.xml b/indra/newview/skins/default/xui/en/floater_stats.xml
index e4f735740b7c15b836ae79bf7d00cf6078f26e10..6f84930c758473e529d6cdb4cf61ecfa47c9a621 100644
--- a/indra/newview/skins/default/xui/en/floater_stats.xml
+++ b/indra/newview/skins/default/xui/en/floater_stats.xml
@@ -35,6 +35,25 @@
                   decimal_digits="1"
                   show_bar="true"
                   show_history="true"/>
+        <stat_bar name="frame_mean"
+                  label="frame (mean)"
+                  unit_label="ms"
+                  stat="frametime"
+                  decimal_digits="1"
+                  show_bar="false"
+                  show_history="false"/>
+        <stat_bar name="frame_median"
+                  label="frame (median)"
+                  unit_label="ms"
+                  stat="frametime"
+                  show_median="true"
+                  decimal_digits="1"
+                  show_bar="false"
+                  show_history="false"/>
+        <stat_bar name="framet_jitter"
+                  label="jitter"
+                  decimal_digits="1"
+                  stat="frametimejitter"/>
        <stat_bar name="bandwidth"
                   label="UDP Data Received"
                   stat="activemessagedatareceived"