diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp
index 27a368df3d0e05ed349dcfa86efd491958f8dc03..69d092de764647a3f4f5a3db49854759b4030c05 100644
--- a/indra/llmessage/llassetstorage.cpp
+++ b/indra/llmessage/llassetstorage.cpp
@@ -398,6 +398,12 @@ BOOL LLAssetStorage::hasLocalAsset(const LLUUID &uuid, const LLAssetType::EType
 bool LLAssetStorage::findInStaticVFSAndInvokeCallback(const LLUUID& uuid, LLAssetType::EType type,
 													  LLGetAssetCallback callback, void *user_data)
 {
+	if (user_data)
+	{
+		// The *user_data should not be passed without a callback to clean it up.
+		llassert(callback != NULL)
+	}
+
 	BOOL exists = mStaticVFS->getExists(uuid, type);
 	if (exists)
 	{
@@ -432,15 +438,26 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, LL
 
 	llinfos << "ASSET_TRACE requesting " << uuid << " type " << LLAssetType::lookup(type) << llendl;
 
+	if (user_data)
+	{
+		// The *user_data should not be passed without a callback to clean it up.
+		llassert(callback != NULL)
+	}
+
 	if (mShutDown)
 	{
 		llinfos << "ASSET_TRACE cancelled " << uuid << " type " << LLAssetType::lookup(type) << " shutting down" << llendl;
-		return; // don't get the asset or do any callbacks, we are shutting down
+
+		if (callback)
+		{
+			callback(mVFS, uuid, type, user_data, LL_ERR_ASSET_REQUEST_FAILED, LL_EXSTAT_NONE);
+		}
+		return;
 	}
-		
+
 	if (uuid.isNull())
 	{
-		// Special case early out for NULL uuid
+		// Special case early out for NULL uuid and for shutting down
 		if (callback)
 		{
 			callback(mVFS, uuid, type, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE, LL_EXSTAT_NULL_UUID);
diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp
index f658287fb1145afd5b9a035cdfd77bf9f0ee6b10..2f9856c650e8fc5e61dada62a80a8924e752563e 100644
--- a/indra/newview/llgesturemgr.cpp
+++ b/indra/newview/llgesturemgr.cpp
@@ -33,8 +33,10 @@
 #include <algorithm>
 
 // library
+#include "llaudioengine.h"
 #include "lldatapacker.h"
 #include "llinventory.h"
+#include "llkeyframemotion.h"
 #include "llmultigesture.h"
 #include "llnotificationsutil.h"
 #include "llstl.h"
@@ -526,6 +528,66 @@ void LLGestureMgr::playGesture(LLMultiGesture* gesture)
 	gesture->mPlaying = TRUE;
 	mPlaying.push_back(gesture);
 
+	// Load all needed assets to minimize the delays
+	// when gesture is playing.
+	for (std::vector<LLGestureStep*>::iterator steps_it = gesture->mSteps.begin();
+		 steps_it != gesture->mSteps.end();
+		 ++steps_it)
+	{
+		LLGestureStep* step = *steps_it;
+		switch(step->getType())
+		{
+		case STEP_ANIMATION:
+			{
+				LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
+				const LLUUID& anim_id = anim_step->mAnimAssetID;
+
+				// Don't request the animation if this step stops it or if it is already in Static VFS
+				if (!(anim_id.isNull()
+					  || anim_step->mFlags & ANIM_FLAG_STOP
+					  || gAssetStorage->hasLocalAsset(anim_id, LLAssetType::AT_ANIMATION)))
+				{
+					mLoadingAssets.insert(anim_id);
+
+					LLUUID* id = new LLUUID(gAgentID);
+					gAssetStorage->getAssetData(anim_id,
+									LLAssetType::AT_ANIMATION,
+									onAssetLoadComplete,
+									(void *)id,
+									TRUE);
+				}
+				break;
+			}
+		case STEP_SOUND:
+			{
+				LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
+				const LLUUID& sound_id = sound_step->mSoundAssetID;
+				if (!(sound_id.isNull()
+					  || gAssetStorage->hasLocalAsset(sound_id, LLAssetType::AT_SOUND)))
+				{
+					mLoadingAssets.insert(sound_id);
+
+					gAssetStorage->getAssetData(sound_id,
+									LLAssetType::AT_SOUND,
+									onAssetLoadComplete,
+									NULL,
+									TRUE);
+				}
+				break;
+			}
+		case STEP_CHAT:
+		case STEP_WAIT:
+		case STEP_EOF:
+			{
+				break;
+			}
+		default:
+			{
+				llwarns << "Unknown gesture step type: " << step->getType() << llendl;
+			}
+		}
+	}
+
 	// And get it going
 	stepGesture(gesture);
 
@@ -741,7 +803,7 @@ void LLGestureMgr::stepGesture(LLMultiGesture* gesture)
 	{
 		return;
 	}
-	if (!isAgentAvatarValid()) return;
+	if (!isAgentAvatarValid() || hasLoadingAssets(gesture)) return;
 
 	// Of the ones that started playing, have any stopped?
 
@@ -1091,6 +1153,98 @@ void LLGestureMgr::onLoadComplete(LLVFS *vfs,
 	}
 }
 
+// static
+void LLGestureMgr::onAssetLoadComplete(LLVFS *vfs,
+									   const LLUUID& asset_uuid,
+									   LLAssetType::EType type,
+									   void* user_data, S32 status, LLExtStat ext_status)
+{
+	LLGestureMgr& self = LLGestureMgr::instance();
+
+	// Complete the asset loading process depending on the type and
+	// remove the asset id from pending downloads list.
+	switch(type)
+	{
+	case LLAssetType::AT_ANIMATION:
+		{
+			LLKeyframeMotion::onLoadComplete(vfs, asset_uuid, type, user_data, status, ext_status);
+
+			self.mLoadingAssets.erase(asset_uuid);
+
+			break;
+		}
+	case LLAssetType::AT_SOUND:
+		{
+			LLAudioEngine::assetCallback(vfs, asset_uuid, type, user_data, status, ext_status);
+
+			self.mLoadingAssets.erase(asset_uuid);
+
+			break;
+		}
+	default:
+		{
+			llwarns << "Unexpected asset type: " << type << llendl;
+
+			// We don't want to return from this callback without
+			// an animation or sound callback being fired
+			// and *user_data handled to avoid memory leaks.
+			llassert(type == LLAssetType::AT_ANIMATION || type == LLAssetType::AT_SOUND);
+		}
+	}
+}
+
+// static
+bool LLGestureMgr::hasLoadingAssets(LLMultiGesture* gesture)
+{
+	LLGestureMgr& self = LLGestureMgr::instance();
+
+	for (std::vector<LLGestureStep*>::iterator steps_it = gesture->mSteps.begin();
+		 steps_it != gesture->mSteps.end();
+		 ++steps_it)
+	{
+		LLGestureStep* step = *steps_it;
+		switch(step->getType())
+		{
+		case STEP_ANIMATION:
+			{
+				LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
+				const LLUUID& anim_id = anim_step->mAnimAssetID;
+
+				if (!(anim_id.isNull()
+					  || anim_step->mFlags & ANIM_FLAG_STOP
+					  || self.mLoadingAssets.find(anim_id) == self.mLoadingAssets.end()))
+				{
+					return true;
+				}
+				break;
+			}
+		case STEP_SOUND:
+			{
+				LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
+				const LLUUID& sound_id = sound_step->mSoundAssetID;
+
+				if (!(sound_id.isNull()
+					  || self.mLoadingAssets.find(sound_id) == self.mLoadingAssets.end()))
+				{
+					return true;
+				}
+				break;
+			}
+		case STEP_CHAT:
+		case STEP_WAIT:
+		case STEP_EOF:
+			{
+				break;
+			}
+		default:
+			{
+				llwarns << "Unknown gesture step type: " << step->getType() << llendl;
+			}
+		}
+	}
+
+	return false;
+}
 
 void LLGestureMgr::stopGesture(LLMultiGesture* gesture)
 {
diff --git a/indra/newview/llgesturemgr.h b/indra/newview/llgesturemgr.h
index b9935efeb3c25a009343fbfae61b00e47270887b..5930841cbc1e70302eb0ca64c34f2aca098bcc1a 100644
--- a/indra/newview/llgesturemgr.h
+++ b/indra/newview/llgesturemgr.h
@@ -154,9 +154,20 @@ class LLGestureMgr : public LLSingleton<LLGestureMgr>, public LLInventoryFetchIt
 
 	// Used by loadGesture
 	static void onLoadComplete(LLVFS *vfs,
-						   const LLUUID& asset_uuid,
-						   LLAssetType::EType type,
-						   void* user_data, S32 status, LLExtStat ext_status);
+							   const LLUUID& asset_uuid,
+							   LLAssetType::EType type,
+							   void* user_data, S32 status, LLExtStat ext_status);
+
+	// Used by playGesture to load an asset file
+	// required to play a gesture step
+	static void onAssetLoadComplete(LLVFS *vfs,
+									const LLUUID& asset_uuid,
+									LLAssetType::EType type,
+									void* user_data, S32 status, LLExtStat ext_status);
+
+	// Checks whether all animation and sound assets
+	// needed to play a gesture are loaded.
+	static bool hasLoadingAssets(LLMultiGesture* gesture);
 
 private:
 	// Active gestures.
@@ -172,6 +183,8 @@ class LLGestureMgr : public LLSingleton<LLGestureMgr>, public LLInventoryFetchIt
 	callback_map_t mCallbackMap;
 	std::vector<LLMultiGesture*> mPlaying;	
 	BOOL mValid;
+
+	std::set<LLUUID> mLoadingAssets;
 };
 
 #endif