diff --git a/indra/newview/llfloaterlagmeter.cpp b/indra/newview/llfloaterlagmeter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cf50cf61902222d83ba7d7d60cdd18dfa74702af
--- /dev/null
+++ b/indra/newview/llfloaterlagmeter.cpp
@@ -0,0 +1,287 @@
+/** 
+ * @file llfloaterlagmeter.cpp
+ * @brief The "Lag-o-Meter" floater used to tell users what is causing lag.
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterlagmeter.h"
+
+#include "llvieweruictrlfactory.h"
+#include "llviewerstats.h"
+#include "llviewerimage.h"
+#include "llviewercontrol.h"
+#include "viewer.h"
+#include "lltexturefetch.h"
+
+#include "llbutton.h"
+#include "llfocusmgr.h"
+#include "lltextbox.h"
+
+const LLString LAG_CRITICAL_IMAGE_NAME = "lag_status_critical.tga";
+const LLString LAG_WARNING_IMAGE_NAME  = "lag_status_warning.tga";
+const LLString LAG_GOOD_IMAGE_NAME     = "lag_status_good.tga";
+
+LLFloaterLagMeter * LLFloaterLagMeter::sInstance = NULL;
+
+LLFloaterLagMeter::LLFloaterLagMeter()
+	:	LLFloater("floater_lagmeter")
+{
+	gUICtrlFactory->buildFloater(this, "floater_lagmeter.xml");
+
+	mClientButton = LLUICtrlFactory::getButtonByName(this, "client_lagmeter");
+	mClientText = LLUICtrlFactory::getTextBoxByName(this, "client_text");
+	mClientCause = LLUICtrlFactory::getTextBoxByName(this, "client_lag_cause");
+
+	mNetworkButton = LLUICtrlFactory::getButtonByName(this, "network_lagmeter");
+	mNetworkText = LLUICtrlFactory::getTextBoxByName(this, "network_text");
+	mNetworkCause = LLUICtrlFactory::getTextBoxByName(this, "network_lag_cause");
+
+	mServerButton = LLUICtrlFactory::getButtonByName(this, "server_lagmeter");
+	mServerText = LLUICtrlFactory::getTextBoxByName(this, "server_text");
+	mServerCause = LLUICtrlFactory::getTextBoxByName(this, "server_lag_cause");
+
+	childSetFocus("client_help", TRUE);
+
+	LLString config_string = childGetText("client_frame_rate_critical_fps");
+	mClientFrameTimeCritical = 1.0f / (float)atof( config_string.c_str() );
+	config_string = childGetText("client_frame_rate_warning_fps");
+	mClientFrameTimeWarning = 1.0f / (float)atof( config_string.c_str() );
+
+	config_string = childGetText("network_packet_loss_critical_pct");
+	mNetworkPacketLossCritical = (float)atof( config_string.c_str() );
+	config_string = childGetText("network_packet_loss_warning_pct");
+	mNetworkPacketLossWarning = (float)atof( config_string.c_str() );
+
+	config_string = childGetText("server_frame_rate_critical_fps");
+	mServerFrameTimeCritical = 1000.0f / (float)atof( config_string.c_str() );
+	config_string = childGetText("server_frame_rate_warning_fps");
+	mServerFrameTimeWarning = 1000.0f / (float)atof( config_string.c_str() );
+	config_string = childGetText("server_single_process_max_time_ms");
+	mServerSingleProcessMaxTime = (float)atof( config_string.c_str() );
+
+	mMinimized = false;
+	config_string = childGetText("max_width_px");
+	mMaxWidth = atoi( config_string.c_str() );
+	config_string = childGetText("min_width_px");
+	mMinWidth = atoi( config_string.c_str() );
+
+	childSetTextArg("client_frame_time_critical_msg", "[CLIENT_FRAME_RATE_CRITICAL]", childGetText("client_frame_rate_critical_fps"));
+	childSetTextArg("client_frame_time_warning_msg", "[CLIENT_FRAME_RATE_CRITICAL]", childGetText("client_frame_rate_critical_fps"));
+	childSetTextArg("client_frame_time_warning_msg", "[CLIENT_FRAME_RATE_WARNING]", childGetText("client_frame_rate_warning_fps"));
+
+	childSetTextArg("network_packet_loss_critical_msg", "[NETWORK_PACKET_LOSS_CRITICAL]", childGetText("network_packet_loss_critical_pct"));
+	childSetTextArg("network_packet_loss_warning_msg", "[NETWORK_PACKET_LOSS_CRITICAL]", childGetText("network_packet_loss_critical_pct"));
+	childSetTextArg("network_packet_loss_warning_msg", "[NETWORK_PACKET_LOSS_WARNING]", childGetText("network_packet_loss_warning_pct"));
+
+	childSetTextArg("server_frame_time_critical_msg", "[SERVER_FRAME_RATE_CRITICAL]", childGetText("server_frame_rate_critical_fps"));
+	childSetTextArg("server_frame_time_warning_msg", "[SERVER_FRAME_RATE_CRITICAL]", childGetText("server_frame_rate_critical_fps"));
+	childSetTextArg("server_frame_time_warning_msg", "[SERVER_FRAME_RATE_WARNING]", childGetText("server_frame_rate_warning_fps"));
+
+	childSetAction("minimize", onClickShrink, this);
+}
+
+LLFloaterLagMeter::~LLFloaterLagMeter()
+{
+	sInstance = NULL;
+}
+
+void LLFloaterLagMeter::draw()
+{
+	determineClient();
+	determineNetwork();
+	determineServer();
+
+	LLFloater::draw();
+}
+
+//static
+void LLFloaterLagMeter::show(void *data)
+{
+	if(!sInstance) sInstance = new LLFloaterLagMeter();
+	sInstance->open();
+}
+
+void LLFloaterLagMeter::determineClient()
+{
+	F32 client_frame_time = gViewerStats->mFPSStat.getMeanDuration();
+	bool find_cause = false;
+
+	if (!gFocusMgr.getAppHasFocus())
+	{
+		mClientButton->setImageUnselected(LAG_GOOD_IMAGE_NAME);
+		mClientText->setText( childGetText("client_frame_time_window_bg_msg") );
+		mClientCause->setText( LLString::null );
+	}
+	else if(client_frame_time >= mClientFrameTimeCritical)
+	{
+		mClientButton->setImageUnselected(LAG_CRITICAL_IMAGE_NAME);
+		mClientText->setText( childGetText("client_frame_time_critical_msg") );
+		find_cause = true;
+	}
+	else if(client_frame_time >= mClientFrameTimeWarning)
+	{
+		mClientButton->setImageUnselected(LAG_WARNING_IMAGE_NAME);
+		mClientText->setText( childGetText("client_frame_time_warning_msg") );
+		find_cause = true;
+	}
+	else
+	{
+		mClientButton->setImageUnselected(LAG_GOOD_IMAGE_NAME);
+		mClientText->setText( childGetText("client_frame_time_normal_msg") );
+		mClientCause->setText( LLString::null );
+	}	
+
+	if(find_cause)
+	{
+		if(gSavedSettings.getF32("RenderFarClip") > 128)
+		{
+			mClientCause->setText( childGetText("client_draw_distance_cause_msg") );
+		}
+		else if(gTextureFetch->getNumRequests() > 2)
+		{
+			mClientCause->setText( childGetText("client_texture_loading_cause_msg") );
+		}
+		else if(LLViewerImage::sBoundTextureMemory > LLViewerImage::sMaxBoundTextureMem)
+		{
+			mClientCause->setText( childGetText("client_texture_memory_cause_msg") );
+		}
+		else 
+		{
+			mClientCause->setText( childGetText("client_complex_objects_cause_msg") );
+		}
+	}
+}
+
+void LLFloaterLagMeter::determineNetwork()
+{
+	F32 packet_loss = gViewerStats->mPacketsLostPercentStat.getMean();
+	bool find_cause = false;
+
+	if(packet_loss >= mNetworkPacketLossCritical)
+	{
+		mNetworkButton->setImageUnselected(LAG_CRITICAL_IMAGE_NAME);
+		mNetworkText->setText( childGetText("network_packet_loss_critical_msg") );
+		find_cause = true;
+	}
+	else if(packet_loss >= mNetworkPacketLossWarning)
+	{
+		mNetworkButton->setImageUnselected(LAG_WARNING_IMAGE_NAME);
+		mNetworkText->setText( childGetText("network_packet_loss_warning_msg") );
+		find_cause = true;
+	}
+	else
+	{
+		mNetworkButton->setImageUnselected(LAG_GOOD_IMAGE_NAME);
+		mNetworkText->setText( childGetText("network_packet_loss_normal_msg") );
+		mNetworkCause->setText( LLString::null );
+	}
+
+	if(find_cause)
+	{
+		mNetworkCause->setText( childGetText("network_cause_msg") );
+	}
+}
+
+void LLFloaterLagMeter::determineServer()
+{
+	F32 sim_frame_time = gViewerStats->mSimFrameMsec.getCurrent();
+	bool find_cause = false;
+
+	if(sim_frame_time >= mServerFrameTimeCritical)
+	{
+		mServerButton->setImageUnselected(LAG_CRITICAL_IMAGE_NAME);
+		mServerText->setText( childGetText("server_frame_time_critical_msg") );
+		find_cause = true;
+	}
+	else if(sim_frame_time >= mServerFrameTimeWarning)
+	{
+		mServerButton->setImageUnselected(LAG_WARNING_IMAGE_NAME);
+		mServerText->setText( childGetText("server_frame_time_warning_msg") );
+		find_cause = true;
+	}
+	else
+	{
+		mServerButton->setImageUnselected(LAG_GOOD_IMAGE_NAME);
+		mServerText->setText( childGetText("server_frame_time_normal_msg") );
+		mServerCause->setText( LLString::null );
+	}	
+
+	if(find_cause)
+	{
+		if(gViewerStats->mSimSimPhysicsMsec.getCurrent() > mServerSingleProcessMaxTime)
+		{
+			mServerCause->setText( childGetText("server_physics_cause_msg") );
+		}
+		else if(gViewerStats->mSimScriptMsec.getCurrent() > mServerSingleProcessMaxTime)
+		{
+			mServerCause->setText( childGetText("server_scripts_cause_msg") );
+		}
+		else if(gViewerStats->mSimNetMsec.getCurrent() > mServerSingleProcessMaxTime)
+		{
+			mServerCause->setText( childGetText("server_net_cause_msg") );
+		}
+		else if(gViewerStats->mSimAgentMsec.getCurrent() > mServerSingleProcessMaxTime)
+		{
+			mServerCause->setText( childGetText("server_agent_cause_msg") );
+		}
+		else if(gViewerStats->mSimImagesMsec.getCurrent() > mServerSingleProcessMaxTime)
+		{
+			mServerCause->setText( childGetText("server_images_cause_msg") );
+		}
+		else
+		{
+			mServerCause->setText( childGetText("server_generic_cause_msg") );
+		}
+	}
+}
+
+//static
+void LLFloaterLagMeter::onClickShrink(void * data)
+{
+	LLFloaterLagMeter * self = (LLFloaterLagMeter*)data;
+
+	if(self->mMinimized)
+	{
+		self->setTitle( self->childGetText("max_title_msg") );
+		self->reshape(self->mMaxWidth, self->getRect().getHeight());
+		
+		self->childSetText("client", self->childGetText("client_text_msg") + ":");
+		self->childSetText("network", self->childGetText("network_text_msg") + ":");
+		self->childSetText("server", self->childGetText("server_text_msg") + ":");
+
+		LLButton * button = (LLButton*)self->getChildByName("minimize");
+		// usually "<<"
+		button->setLabel( self->childGetText("smaller_label") );
+	}
+	else
+	{
+		self->setTitle( self->childGetText("min_title_msg") );
+		self->reshape(self->mMinWidth, self->getRect().getHeight());
+		
+		self->childSetText("client", self->childGetText("client_text_msg") );
+		self->childSetText("network", self->childGetText("network_text_msg") );
+		self->childSetText("server", self->childGetText("server_text_msg") );
+
+		LLButton * button = (LLButton*)self->getChildByName("minimize");
+		// usually ">>"
+		button->setLabel( self->childGetText("bigger_label") );
+	}
+
+	self->mClientText->setVisible(self->mMinimized);
+	self->mClientCause->setVisible(self->mMinimized);
+	self->childSetVisible("client_help", self->mMinimized);
+
+	self->mNetworkText->setVisible(self->mMinimized);
+	self->mNetworkCause->setVisible(self->mMinimized);
+	self->childSetVisible("network_help", self->mMinimized);
+
+	self->mServerText->setVisible(self->mMinimized);
+	self->mServerCause->setVisible(self->mMinimized);
+	self->childSetVisible("server_help", self->mMinimized);
+
+	self->mMinimized = !self->mMinimized;
+}
diff --git a/indra/newview/llfloaterlagmeter.h b/indra/newview/llfloaterlagmeter.h
new file mode 100644
index 0000000000000000000000000000000000000000..72766fec852dab83e96811d7dc7d37cf4d70d61f
--- /dev/null
+++ b/indra/newview/llfloaterlagmeter.h
@@ -0,0 +1,55 @@
+/** 
+ * @file llfloaterlagmeter.h
+ * @brief The "Lag-o-Meter" floater used to tell users what is causing lag.
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLFLOATERLAGMETER_H
+#define LLFLOATERLAGMETER_H
+
+#include "llfloater.h"
+
+class LLFloaterLagMeter : public LLFloater
+{
+public:
+	/*virtual*/ void draw();
+	static void show(void*);
+
+private:
+	LLFloaterLagMeter();
+	/*virtual*/ ~LLFloaterLagMeter();
+
+	void determineClient();
+	void determineNetwork();
+	void determineServer();
+
+	static void onClickShrink(void * data);
+
+	bool mMinimized;
+	S32 mMaxWidth, mMinWidth;
+
+	F32 mClientFrameTimeCritical;
+	F32 mClientFrameTimeWarning;
+	LLButton * mClientButton;
+	LLTextBox * mClientText;
+	LLTextBox * mClientCause;
+
+	F32 mNetworkPacketLossCritical;
+	F32 mNetworkPacketLossWarning;
+	LLButton * mNetworkButton;
+	LLTextBox * mNetworkText;
+	LLTextBox * mNetworkCause;
+
+	F32 mServerFrameTimeCritical;
+	F32 mServerFrameTimeWarning;
+	F32 mServerSingleProcessMaxTime;
+	LLButton * mServerButton;
+	LLTextBox * mServerText;
+	LLTextBox * mServerCause;
+
+	static LLFloaterLagMeter * sInstance;
+};
+
+#endif
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 21a334e70b47d2bae0b79f3463b15007c14f632d..c242d855f134f52dd75fa8a8af2b12a12c259851 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -85,6 +85,7 @@
 #include "llfloaterhtmlhelp.h"
 #include "llfloaterhtmlfind.h"
 #include "llfloaterinspect.h"
+#include "llfloaterlagmeter.h"
 #include "llfloaterland.h"
 #include "llfloaterlandholdings.h"
 #include "llfloatermap.h"
@@ -5514,6 +5515,10 @@ class LLShowFloater : public view_listener_t
 				LLFloaterBump::show(NULL);
 			}
 		}
+		else if (floater_name == "lag meter")
+		{
+			LLFloaterLagMeter::show(NULL);
+		}
 		else if (floater_name == "bug reporter")
 		{
 			// Prevent menu from appearing in screen shot.
@@ -7760,6 +7765,7 @@ void initialize_menus()
 
 	// Help menu
 	addMenu(new LLHelpMOTD(), "Help.MOTD");
+	// most items use the ShowFloater method
 
 	// Self pie menu
 	addMenu(new LLSelfStandUp(), "Self.StandUp");
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index b19b676fd60d8676c239c65f3ec5bc364937bec6..897831afc043673393fe9f1a74dbf112a5bba9d5 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -1800,6 +1800,8 @@ void LLViewerWindow::adjustRectanglesForFirstUse(const LLRect& window)
 
 	adjust_rect_top_right("FloaterMapRect", window);
 
+	adjust_rect_top_right("FloaterLagMeter", window);
+
 	adjust_rect_top_left("FloaterBuildOptionsRect", window);
 
 	// bottom-right