diff --git a/src/include/particle.h b/src/include/particle.h
index 3c5e37282..9338377fa 100644
--- a/src/include/particle.h
+++ b/src/include/particle.h
@@ -43,17 +43,7 @@ struct CPosition {
 	float y;
 };
 
-class Animation
-{
-public:
-	virtual ~Animation() {}
-	virtual void draw(int x, int y) = 0;
-	virtual void update(int ticks) = 0;
-	virtual bool isFinished() = 0;
-	virtual Animation *clone() = 0;
-};
-
-class GraphicAnimation : public Animation
+class GraphicAnimation
 {
 	CGraphic *g;
 	int ticksPerFrame;
@@ -61,24 +51,24 @@ class GraphicAnimation : public Animation
 	int currTicks;
 public:
 	GraphicAnimation(CGraphic *g, int ticksPerFrame);
-	virtual ~GraphicAnimation() {}
+	~GraphicAnimation() {}
 
 	/**
 	**  Draw the current frame of the animation.
 	**  @param x x screen coordinate where to draw the animation.
 	**  @param y y screen coordinate where to draw the animation.
 	*/
-	virtual void draw(int x, int y);
+	void draw(int x, int y);
 
 	/**
 	**  Update the animation.
 	**  @param ticks the number of ticks elapsed since the last call.
 	*/
-	virtual void update(int ticks);
+	void update(int ticks);
 
-	virtual bool isFinished();
-
-	virtual Animation *clone();
+	bool isFinished();
+	bool isVisible(const CViewport &vp, const CPosition &pos);
+	GraphicAnimation *clone();
 };
 
 
@@ -92,8 +82,9 @@ public:
 	{}
 	virtual ~CParticle() {}
 
-	virtual void draw() {}
-	virtual void update(int) {}
+	virtual bool isVisible(const CViewport &vp) const = 0;
+	virtual void draw() = 0;
+	virtual void update(int) = 0;
 
 	inline void destroy() { destroyed = true; }
 	inline bool isDestroyed() { return destroyed; }
@@ -109,15 +100,16 @@ protected:
 class StaticParticle : public CParticle
 {
 public:
-	StaticParticle(CPosition position, Animation *flame);
+	StaticParticle(CPosition position, GraphicAnimation *flame);
 	virtual ~StaticParticle();
 
+	virtual bool isVisible(const CViewport &vp) const;
 	virtual void draw();
 	virtual void update(int ticks);
 	virtual CParticle *clone();
 
 protected:
-	Animation *animation;
+	GraphicAnimation *animation;
 };
 
 
@@ -125,12 +117,13 @@ protected:
 class CChunkParticle : public CParticle
 {
 public:
-	CChunkParticle(CPosition position, Animation *smokeAnimation, Animation *debrisAnimation,
-				   Animation *destroyAnimation,
+	CChunkParticle(CPosition position, GraphicAnimation *smokeAnimation, GraphicAnimation *debrisAnimation,
+				   GraphicAnimation *destroyAnimation,
 				   int minVelocity = 0, int maxVelocity = 400,
 				   int minTrajectoryAngle = 77, int maxTTL = 0);
 	virtual ~CChunkParticle();
 
+	virtual bool isVisible(const CViewport &vp) const;
 	virtual void draw();
 	virtual void update(int ticks);
 	virtual CParticle *clone();
@@ -147,9 +140,9 @@ protected:
 	int maxVelocity;
 	int minTrajectoryAngle;
 	float height;
-	Animation *debrisAnimation;
-	Animation *smokeAnimation;
-	Animation *destroyAnimation;
+	GraphicAnimation *debrisAnimation;
+	GraphicAnimation *smokeAnimation;
+	GraphicAnimation *destroyAnimation;
 
 	struct {
 		float x;
@@ -162,15 +155,16 @@ protected:
 class CSmokeParticle : public CParticle
 {
 public:
-	CSmokeParticle(CPosition position, Animation *animation, float speedx = 0, float speedy = -22.0f);
+	CSmokeParticle(CPosition position, GraphicAnimation *animation, float speedx = 0, float speedy = -22.0f);
 	virtual ~CSmokeParticle();
 
+	virtual bool isVisible(const CViewport &vp) const;
 	virtual void draw();
 	virtual void update(int ticks);
 	virtual CParticle *clone();
 
 protected:
-	Animation *puff;
+	GraphicAnimation *puff;
 	struct {
 		float x;
 		float y;
@@ -180,15 +174,16 @@ protected:
 class CRadialParticle : public CParticle
 {
 public:
-	CRadialParticle(CPosition position, Animation *animation, int maxSpeed);
+	CRadialParticle(CPosition position, GraphicAnimation *animation, int maxSpeed);
 	virtual ~CRadialParticle();
 
+	virtual bool isVisible(const CViewport &vp) const;
 	virtual void draw();
 	virtual void update(int ticks);
 	virtual CParticle *clone();
 
 protected:
-	Animation *animation;
+	GraphicAnimation *animation;
 	float direction;
 	int speed;
 	int maxSpeed;
diff --git a/src/particle/chunkparticle.cpp b/src/particle/chunkparticle.cpp
index 8e99bbb66..e858974a5 100644
--- a/src/particle/chunkparticle.cpp
+++ b/src/particle/chunkparticle.cpp
@@ -44,8 +44,8 @@ static inline float deg2rad(int degrees)
 }
 
 
-CChunkParticle::CChunkParticle(CPosition position, Animation *smokeAnimation, Animation *debrisAnimation,
-							   Animation *destroyAnimation,
+CChunkParticle::CChunkParticle(CPosition position, GraphicAnimation *smokeAnimation, GraphicAnimation *debrisAnimation,
+							   GraphicAnimation *destroyAnimation,
 							   int minVelocity, int maxVelocity, int minTrajectoryAngle, int maxTTL) :
 	CParticle(position), initialPos(position), maxTTL(maxTTL), nextSmokeTicks(0),
 	age(0), height(0.f)
@@ -81,6 +81,12 @@ static float calculateScreenPos(float posy, float height)
 	return posy - height * 0.2f;
 }
 
+
+bool CChunkParticle::isVisible(const CViewport &vp) const
+{
+	return debrisAnimation && debrisAnimation->isVisible(vp, pos);
+}
+
 void CChunkParticle::draw()
 {
 	CPosition screenPos = ParticleManager.getScreenPos(pos);
@@ -105,7 +111,7 @@ void CChunkParticle::update(int ticks)
 	if (age >= lifetime) {
 		if (destroyAnimation) {
 			CPosition p(pos.x, calculateScreenPos(pos.y, height));
-			Animation *destroyanimation = destroyAnimation->clone();
+			GraphicAnimation *destroyanimation = destroyAnimation->clone();
 			StaticParticle *destroy = new StaticParticle(p, destroyanimation);
 			ParticleManager.add(destroy);
 		}
@@ -119,7 +125,7 @@ void CChunkParticle::update(int ticks)
 
 	if (age > nextSmokeTicks) {
 		CPosition p(pos.x, calculateScreenPos(pos.y, height));
-		Animation *smokeanimation = smokeAnimation->clone();
+		GraphicAnimation *smokeanimation = smokeAnimation->clone();
 		CSmokeParticle *smoke = new CSmokeParticle(p, smokeanimation);
 		ParticleManager.add(smoke);
 
@@ -128,7 +134,7 @@ void CChunkParticle::update(int ticks)
 
 	debrisAnimation->update(ticks);
 	if (debrisAnimation->isFinished()) {
-		Animation *debrisanimation = debrisAnimation->clone();
+		GraphicAnimation *debrisanimation = debrisAnimation->clone();
 		delete debrisAnimation;
 		debrisAnimation = debrisanimation;
 	}
diff --git a/src/particle/graphicanimation.cpp b/src/particle/graphicanimation.cpp
index a4537a172..7e83fc857 100644
--- a/src/particle/graphicanimation.cpp
+++ b/src/particle/graphicanimation.cpp
@@ -30,7 +30,12 @@
 //@{
 
 #include "stratagus.h"
+
 #include "particle.h"
+
+#include "map.h"
+#include "player.h"
+#include "ui.h"
 #include "video.h"
 
 GraphicAnimation::GraphicAnimation(CGraphic *g, int ticksPerFrame) :
@@ -61,7 +66,37 @@ bool GraphicAnimation::isFinished()
 	return currentFrame >= g->NumFrames;
 }
 
-Animation *GraphicAnimation::clone()
+bool GraphicAnimation::isVisible(const CViewport &vp, const CPosition &pos)
+{
+	// invisible graphics always invisible
+	if (!g) {
+		return false;
+	}
+
+	PixelSize graphicSize(g->Width, g->Height);
+	PixelDiff margin(PixelTileSize.x - 1, PixelTileSize.y - 1);
+	PixelPos position(pos.x, pos.y);
+	Vec2i minPos = Map.MapPixelPosToTilePos(position);
+	Vec2i maxPos = Map.MapPixelPosToTilePos(position + graphicSize + margin);
+	Map.Clamp(minPos);
+	Map.Clamp(maxPos);
+
+	if (!vp.AnyMapAreaVisibleInViewport(minPos, maxPos)) {
+		return false;
+	}
+
+	Vec2i p;
+	for (p.x = minPos.x; p.x <= maxPos.x; ++p.x) {
+		for (p.y = minPos.y; p.y <= maxPos.y; ++p.y) {
+			if (ReplayRevealMap || Map.Field(p)->playerInfo.IsTeamVisible(*ThisPlayer)) {
+				return true;
+			}
+		}
+	}
+	return false;
+}
+
+GraphicAnimation *GraphicAnimation::clone()
 {
 	return new GraphicAnimation(g, ticksPerFrame);
 }
diff --git a/src/particle/particlemanager.cpp b/src/particle/particlemanager.cpp
index 8fe974676..8025f3394 100644
--- a/src/particle/particlemanager.cpp
+++ b/src/particle/particlemanager.cpp
@@ -31,8 +31,8 @@
 
 #include "stratagus.h"
 #include "particle.h"
-#include "video.h"
 #include "ui.h"
+#include "video.h"
 
 
 CParticleManager ParticleManager;
@@ -76,7 +76,9 @@ void CParticleManager::draw(const CViewport &vp)
 
 	std::vector<CParticle *>::iterator i;
 	for (i = particles.begin(); i != particles.end(); ++i) {
-		(*i)->draw();
+		if ((*i)->isVisible(vp)) {
+			(*i)->draw();
+		}
 	}
 
 	this->vp = NULL;
@@ -117,4 +119,4 @@ CPosition CParticleManager::getScreenPos(const CPosition &pos) const
 	return CPosition(screenPixelPos.x, screenPixelPos.y);
 }
 
-//@}
+//@}
\ No newline at end of file
diff --git a/src/particle/radialparticle.cpp b/src/particle/radialparticle.cpp
index c5b7d94c1..e27008312 100644
--- a/src/particle/radialparticle.cpp
+++ b/src/particle/radialparticle.cpp
@@ -34,7 +34,7 @@
 #include "stratagus.h"
 #include "particle.h"
 
-CRadialParticle::CRadialParticle(CPosition position, Animation *animation, int maxSpeed) :
+CRadialParticle::CRadialParticle(CPosition position, GraphicAnimation *animation, int maxSpeed) :
 	CParticle(position)
 {
 	Assert(animation);
@@ -52,6 +52,11 @@ CRadialParticle::~CRadialParticle()
 	delete animation;
 }
 
+bool CRadialParticle::isVisible(const CViewport &vp) const
+{
+	return animation && animation->isVisible(vp, pos);
+}
+
 void CRadialParticle::draw()
 {
 	CPosition screenPos = ParticleManager.getScreenPos(pos);
diff --git a/src/particle/smokeparticle.cpp b/src/particle/smokeparticle.cpp
index ff6720e78..e0cbdac18 100644
--- a/src/particle/smokeparticle.cpp
+++ b/src/particle/smokeparticle.cpp
@@ -35,7 +35,7 @@
 
 
 
-CSmokeParticle::CSmokeParticle(CPosition position, Animation *smoke,
+CSmokeParticle::CSmokeParticle(CPosition position, GraphicAnimation *smoke,
 							   float speedx, float speedy) :
 	CParticle(position)
 {
@@ -51,6 +51,11 @@ CSmokeParticle::~CSmokeParticle()
 	delete puff;
 }
 
+bool CSmokeParticle::isVisible(const CViewport &vp) const
+{
+	return puff && puff->isVisible(vp, pos);
+}
+
 void CSmokeParticle::draw()
 {
 	CPosition screenPos = ParticleManager.getScreenPos(pos);
diff --git a/src/particle/staticparticle.cpp b/src/particle/staticparticle.cpp
index a32ffec2b..e0cfa6f54 100644
--- a/src/particle/staticparticle.cpp
+++ b/src/particle/staticparticle.cpp
@@ -33,7 +33,7 @@
 #include "particle.h"
 
 
-StaticParticle::StaticParticle(CPosition position, Animation *animation) :
+StaticParticle::StaticParticle(CPosition position, GraphicAnimation *animation) :
 	CParticle(position)
 {
 	Assert(animation);
@@ -45,6 +45,11 @@ StaticParticle::~StaticParticle()
 	delete animation;
 }
 
+bool StaticParticle::isVisible(const CViewport &vp) const
+{
+	return animation && animation->isVisible(vp, pos);
+}
+
 void StaticParticle::draw()
 {
 	CPosition screenPos = ParticleManager.getScreenPos(pos);
diff --git a/src/tolua/particle.pkg b/src/tolua/particle.pkg
index b9eafc507..3d157495d 100644
--- a/src/tolua/particle.pkg
+++ b/src/tolua/particle.pkg
@@ -7,10 +7,10 @@ struct CPosition
 	float y;
 };
 
-class GraphicAnimation : public Animation
+class GraphicAnimation
 {
 	GraphicAnimation(CGraphic *g, int ticksPerFrame);
-	virtual Animation * clone();
+	virtual GraphicAnimation * clone();
 };
 
 class CParticle
@@ -21,25 +21,25 @@ class CParticle
 class StaticParticle : public CParticle
 {
 public:
-	StaticParticle(CPosition position, Animation *animation);
+	StaticParticle(CPosition position, GraphicAnimation *animation);
 };
 
 class CChunkParticle : public CParticle
 {
 public:
-	CChunkParticle(CPosition position, Animation *smokeAnimation, Animation *debrisAnimation, Animation *destroyAnimation, int minVelocity = 0, int maxVelocity = 400, int minTrajectoryAngle = 77, int maxTTL = 0);
+	CChunkParticle(CPosition position, GraphicAnimation *smokeAnimation, GraphicAnimation *debrisAnimation, GraphicAnimation *destroyAnimation, int minVelocity = 0, int maxVelocity = 400, int minTrajectoryAngle = 77, int maxTTL = 0);
 };
 
 class CSmokeParticle : public CParticle
 {
 public:
-	CSmokeParticle(CPosition position, Animation *animation, float speedx = 0, float speedy = -22.0);
+	CSmokeParticle(CPosition position, GraphicAnimation *animation, float speedx = 0, float speedy = -22.0);
 };
 
 class CRadialParticle : public CParticle
 {
 public:
-	CRadialParticle(CPosition position, Animation *smokeAnimation, int maxSpeed);
+	CRadialParticle(CPosition position, GraphicAnimation *smokeAnimation, int maxSpeed);
 };
 
 class CParticleManager