diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua
index c2dc7514d71118dcdbb8d321ad67549676a59394..a495058d93a36549dfa96157e0fc8222fc033154 100644
--- a/builtin/common/misc_helpers.lua
+++ b/builtin/common/misc_helpers.lua
@@ -237,6 +237,43 @@ function math.sign(x, tolerance)
 	return 0
 end
 
+--------------------------------------------------------------------------------
+-- Video enums and pack function
+
+-- E_BLEND_FACTOR
+minetest.ebf = {			     
+	zero			= 0, -- src & dest (0, 0, 0, 0)
+	one			= 1, -- src & dest (1, 1, 1, 1)
+	dst_color 		= 2, -- src (destR, destG, destB, destA)
+	one_minus_dst_color 	= 3, -- src (1-destR, 1-destG, 1-destB, 1-destA)
+	src_color 		= 4, -- dest (srcR, srcG, srcB, srcA)
+	one_minus_src_color 	= 5, -- dest (1-srcR, 1-srcG, 1-srcB, 1-srcA)
+	src_alpha 		= 6, -- src & dest (srcA, srcA, srcA, srcA)
+	one_minus_src_alpha 	= 7, -- src & dest (1-srcA, 1-srcA, 1-srcA, 1-srcA)
+	dst_alpha 		= 8, -- src & dest (destA, destA, destA, destA)
+	one_minus_dst_alpha 	= 9, -- src & dest (1-destA, 1-destA, 1-destA, 1-destA)
+	src_alpha_saturate  	= 10,-- src (min(srcA, 1-destA), idem, ...) 
+}
+
+-- E_MODULATE_FUNC
+minetest.emfn = {
+	modulate_1x    = 1,
+	modulate_2x    = 2,
+	modulate_4x    = 4,
+}
+
+-- E_ALPHA_SOURCE
+minetest.eas = {
+	none	 = 0,
+	vertex_color = 1,
+	texture	 = 2,
+}
+
+-- BlendFunc = source * sourceFactor + dest * destFactor
+function minetest.pack_texture_blend_func(srcFact, dstFact, modulate, alphaSource) 
+	return alphaSource * 4096 + modulate * 256 + srcFact * 16 + dstFact
+end
+
 --------------------------------------------------------------------------------
 function get_last_folder(text,count)
 	local parts = text:split(DIR_DELIM)
diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index 7d552c98012366f336183860441af302a68e8862..3b3f176346e219f28f26dc50400f15bbd2061771 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -414,6 +414,119 @@ the word "`alpha`", then each texture pixel will contain the RGB of
 `<color>` and the alpha of `<color>` multiplied by the alpha of the
 texture pixel.
 
+Particle blend
+--------------
+Blend function is defined by integer number.
+There is a huge number of acceptable blend modificators.
+Colour of a resulting pixel calculated using formulae:
+
+	red = source_red * source_factor + destination_red * destination_factor
+
+and so on for every channel.
+
+Here is a some examples:
+
+Default value:
+
+	material_type_param = 0,
+
+Use this value to disable blending. Texture will be applied to existing pixels
+using alpha channel of it. Its recomended to use 1-bit alpha
+in that case. This value will leave z-buffer writeable.
+
+Additive blend:
+
+	material_type_param = 12641,
+
+Source = src_alpha, destination = one, alpha source is a texture and 
+vertex_color, modulate_1x.
+Black color is completely transparent, white color is completely opaque.
+Alpha channel still used to calculate result color, but not nessesary.
+'destination = one' means that resulting color will be calculated using 
+overwritten pixels values.
+For example with color of source (our texture) RGBA = (0,192,255,63)
+"blue-cyan", 1/4 opaque.
+and already rendered pixel color (40,192,0) "dark lime green" we will get color:
+
+R = source_red(0) * source_factor(src_alpha=63/255) + 
+	destination_red(40) * destination_factor(one) =
+	0 * 63/255 + 40 * 1 = 40
+
+G = 192 * 63/255 + 192 * 1 = 239
+B = 255 * 63/255 + 0 * 1 = 63
+
+Result: (40,239,63), "green" (a kind of).
+Note, if you made a texture with some kind of shape with colour 662211h 
+it will appear dark red with a single particle, then yellow with a 
+several of them and white if player looking thru a lot of them. With 
+this you could made a nice-looking fire.
+
+Substractive blend:
+	
+	material_type_param = 12548,
+
+Source = zero, destination = src_color, alpha source is a texture and 
+vertex_color, modulate_1x.
+Texture darkness act like an alpha channel.
+Black color is completely opaque, white color is completely transparent.
+'destination = src_color' means that destination in multiplied by
+a source values. 'source = zero' means that source values ignored
+(multiplied by 0).
+
+Invert blend:
+
+	material_type_param = 12597,
+	
+Source = one_minus_dst_color, destination = one_minus_src_alpha, alpha source 
+is a texture and vertex_color, modulate_1x.
+Pixels invert color if source color value is big enough. If not, they just
+black.
+'destination = one_minus_src_alpha' means, that effect is masked by a
+source alpha channel.
+
+You can design and use your own blend using those enum values and function
+'minetest.pack_texture_blend_func'. Returned value of a function is 
+your 'material_type_param'.
+
+A values in a brackets is a multiplicators of a red, green, blue 
+and alpha channels respectively.
+
+* 'minetest.ebf': global table, containing blend factor enum values. Such as:
+    *	zero			= 0  -- src & dest (0, 0, 0, 0)
+    *	one			= 1  -- src & dest (1, 1, 1, 1)
+    *	dst_color 		= 2  -- src (destR, destG, destB, destA)
+    *	one_minus_dst_color 	= 3  -- src (1-destR, 1-destG, 1-destB, 1-destA)
+    *	src_color 		= 4  -- dest (srcR, srcG, srcB, srcA)
+    *	one_minus_src_color 	= 5  -- dest (1-srcR, 1-srcG, 1-srcB, 1-srcA)
+    *	src_alpha 		= 6  -- src & dest (srcA, srcA, srcA, srcA)
+    *	one_minus_src_alpha 	= 7  -- src & dest (1-srcA, 1-srcA, 1-srcA, 1-srcA)
+    *	dst_alpha 		= 8  -- src & dest (destA, destA, destA, destA)
+    *	one_minus_dst_alpha 	= 9  -- src & dest (1-destA, 1-destA, 1-destA, 1-destA)
+    *	src_alpha_saturate  	= 10 -- src (min(srcA, 1-destA), idem, ...) 
+
+* 'minetest.emfn': global table, containing modulate enum values. 
+    * Multiply the components of the arguments, and shift the products to the 
+    * left by x bits for brightening. Contain:
+    *	modulate_1x    = 1 -- no bit shift
+    *	modulate_2x    = 2 -- 1 bits shift
+    *	modulate_4x    = 4 -- 2 bits shift
+
+'modulate_4x' is quite useful when you want to make additive blend stronger
+with a lower amount of particles.
+
+* 'minetest.eas': global table, containing alpha source enum values. Such as:
+    *	none	 	= 0 -- do not use alpha.
+    *	vertex_color 	= 1 -- use vertex color alpha.
+    *	texture	 	= 2 -- use texture alpha.
+
+You can use both 'vertex_color' and 'texture' source by using value 3.
+
+* 'minetest.pack_texture_blend_func(srcFact, dstFact, modulate, alphaSource)': return integer
+    * Pack texture blend funcion variable. Depending from that variable blend 
+    * function will be applied in time of a render poligons with selected material.
+    * Therefore resulting pixel will be 'source * srcFact + destination * dstFact'
+    * Use result of this function as 'material_type_param'.
+
 Sounds
 ------
 Only Ogg Vorbis files are supported.
@@ -3650,7 +3763,7 @@ Definition tables
 
 ### Tile definition
 * `"image.png"`
-* `{name="image.png", animation={Tile Animation definition}}`
+* `{name="image.png", animation={Animation definition}}`
 * `{name="image.png", backface_culling=bool, tileable_vertical=bool,
     tileable_horizontal=bool}`
     * backface culling enabled by default for most nodes
@@ -3661,8 +3774,50 @@ Definition tables
 * deprecated, yet still supported field names:
     * `image` (name)
 
-### Tile animation definition
-* `{type="vertical_frames", aspect_w=16, aspect_h=16, length=3.0}`
+### Animation definition
+
+#### Node animation, particle and particle spawners
+* `{	type="vertical_frames", 
+	aspect_w=16, 
+    --  ^ specify width of a picture in pixels. 
+	aspect_h=16, 
+    --  ^ specify height of a frame in pixels. 
+	length=3.0
+    --  ^ specify full loop length. 
+	first_frame = 0, -- <- only for particles, use
+	min_first_frame = 0, -- <- for particle spawners
+	max_first_frame = 0,
+	loop_animation = true,  -- <- only for particles and particle spawners
+    --  specify if animation should start from beginning after last frame.
+}`
+
+#### Particle and particle spawners only
+* `{	
+	type="2d_animation_sheet", -- <- only for particles and particle spawners
+	vertical_frame_num = 1,
+	horizontal_frame_num = 1,
+    --  ^ specify amount of frames in texture. 
+    --	Can be used both for animation or for texture transform 
+    --	together with first_frame variable.
+    --	A animation texture separated on equal parts of frames,
+    --	by horizontal and vertical numbers. For example with 
+    --	vertical_frame_num = 4 and horizontal_frame_num = 3 we got
+    --	4*3 = 12 frames in total. Animation sequence start from
+    --	left top frame and go on to the right until reach end of
+    --	row. Next row also start from left frame.
+	first_frame = 0, -- <- only for particles, use
+	min_first_frame = 0, -- <- for particle spawners
+	max_first_frame = 0,
+    --  ^ specify first frame to start animation.
+	frame_length = -1,
+    --  ^ specify length of a frame in seconds. Negative and zero values
+    --	disable animation. A sequence with vertical_frame_num = 4 and 
+    --	horizontal_frame_num = 3, first_frame = 4 and frame_length = 0.1 
+    --	will end in (4*3-4)*0.1 = 0.8 seconds.
+	loop_animation = true,
+    --  specify if animation should start from beginning after last frame.
+}`
+    * All settings are optional. Default values is located in this example.
 
 ### Node definition (`register_node`)
 
@@ -4117,6 +4272,20 @@ The Biome API is still in an experimental phase and subject to change.
     --  ^ Uses texture (string)
         playername = "singleplayer"
     --  ^ optional, if specified spawns particle only on the player's client
+	material_type_param = 12641,
+    --  ^ optional, if specified spawns particle with 
+    --  specified material type param and disable z-buffer.
+    --  Some examples:
+    --  Default value: 0,
+    --  Additive blend: 12641,
+    --  Substractive blend: 12548,
+    --  Invert blend: 12597,
+    --  See also "Particle blend".
+    	animation = {Animation definition},
+    --	^ see above. Note, that particle and particle spawners have differences.
+	glow = 15,
+    --  ^ optional, specify particle self-luminescence in darkness. 
+	values may vary from 0 (no glow) to 15 (bright glow). 
     }
 
 ### `ParticleSpawner` definition (`add_particlespawner`)
@@ -4151,6 +4320,20 @@ The Biome API is still in an experimental phase and subject to change.
     --  ^ Uses texture (string)
         playername = "singleplayer"
     --  ^ Playername is optional, if specified spawns particle only on the player's client
+	material_type_param = 12641,
+    --	^ optional, if specified spawns particle with specified material type 
+    --	param and disable z-buffer.
+    --  Some examples:
+    --  Default value: 0,
+    --  Additive blend: 12641,
+    --  Substractive blend: 12548,
+    --  Invert blend: 12597,
+    --  See also "Particle blend".
+    	animation = {Animation definition},
+    --	^ see above. Note, that particle and particle spawners have differences.
+	glow = 15,
+    --  ^ optional, specify particle self-luminescence in darkness. 
+	values may vary from 0 (no glow) to 15 (bright glow).
     }
 
 ### `HTTPRequest` definition (`HTTPApiTable.fetch_async`, `HTTPApiTable.fetch_async`)
diff --git a/src/client.h b/src/client.h
index 9f5bda059ce3c26058263fae3868ff40a8c7bef7..c51daf7bcf77ef3d96eafefba9dabba83b78c4dc 100644
--- a/src/client.h
+++ b/src/client.h
@@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "hud.h"
 #include "particles.h"
 #include "network/networkpacket.h"
+#include "nodedef.h" // AnimationType
 
 struct MeshMakeData;
 class MapBlockMesh;
@@ -185,6 +186,14 @@ struct ClientEvent
 			bool collision_removal;
 			bool vertical;
 			std::string *texture;
+			u32 material_type_param;
+			AnimationType animation_type;
+			u16 vertical_frame_num;
+			u16 horizontal_frame_num;
+			u16 first_frame;
+			float frame_length;
+			bool loop_animation;
+			u8 glow;
 		} spawn_particle;
 		struct{
 			u16 amount;
@@ -205,6 +214,15 @@ struct ClientEvent
 			bool vertical;
 			std::string *texture;
 			u32 id;
+			u32 material_type_param;
+			AnimationType animation_type;
+			u16 vertical_frame_num;
+			u16 horizontal_frame_num;
+			u16 min_first_frame;
+			u16 max_first_frame;
+			float frame_length;
+			bool loop_animation;
+			u8 glow;
 		} add_particlespawner;
 		struct{
 			u32 id;
diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp
index 411982f69109d72b81aa4ac3c45f743ee53782fd..03baf078a039a7683820562f9c331d148e6350bc 100644
--- a/src/network/clientpackethandler.cpp
+++ b/src/network/clientpackethandler.cpp
@@ -896,23 +896,46 @@ void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
 	std::string texture     = deSerializeLongString(is);
 	bool vertical           = false;
 	bool collision_removal  = false;
+	u32 material_type_param = 0;
+	AnimationType animation_type = AT_NONE;
+	u16 vertical_frame_num = 1;
+	u16 horizontal_frame_num = 1;
+	u16 first_frame = 0;
+	float frame_length = -1;
+	bool loop_animation = true;
+	u8 glow = 0;
 	try {
 		vertical = readU8(is);
 		collision_removal = readU8(is);
+		material_type_param = readU32(is);
+		animation_type = (AnimationType)readU8(is);
+		vertical_frame_num = readU16(is);
+		horizontal_frame_num = readU16(is);
+		first_frame = readU16(is);
+		frame_length = readF1000(is);
+		loop_animation = readU8(is);
+		glow = readU8(is);
 	} catch (...) {}
 
 	ClientEvent event;
-	event.type                              = CE_SPAWN_PARTICLE;
-	event.spawn_particle.pos                = new v3f (pos);
-	event.spawn_particle.vel                = new v3f (vel);
-	event.spawn_particle.acc                = new v3f (acc);
-	event.spawn_particle.expirationtime     = expirationtime;
-	event.spawn_particle.size               = size;
-	event.spawn_particle.collisiondetection = collisiondetection;
-	event.spawn_particle.collision_removal  = collision_removal;
-	event.spawn_particle.vertical           = vertical;
-	event.spawn_particle.texture            = new std::string(texture);
-
+	event.type                                 = CE_SPAWN_PARTICLE;
+	event.spawn_particle.pos                   = new v3f (pos);
+	event.spawn_particle.vel                   = new v3f (vel);
+	event.spawn_particle.acc                   = new v3f (acc);
+	event.spawn_particle.expirationtime        = expirationtime;
+	event.spawn_particle.size                  = size;
+	event.spawn_particle.collisiondetection	   = collisiondetection;
+	event.spawn_particle.collision_removal     = collision_removal;
+	event.spawn_particle.vertical              = vertical;
+	event.spawn_particle.texture               = new std::string(texture);
+	event.spawn_particle.material_type_param   = material_type_param;
+	event.spawn_particle.animation_type        = animation_type;
+	event.spawn_particle.vertical_frame_num    = vertical_frame_num;
+	event.spawn_particle.horizontal_frame_num  = horizontal_frame_num;
+	event.spawn_particle.first_frame           = first_frame;
+	event.spawn_particle.frame_length          = frame_length;
+	event.spawn_particle.loop_animation        = loop_animation;
+	event.spawn_particle.glow                  = glow;
 	m_client_event_queue.push(event);
 }
 
@@ -932,6 +955,15 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
 	float maxsize;
 	bool collisiondetection;
 	u32 id;
+	u32 material_type_param = 0;
+	u8 animation_type = (u8)AT_NONE;
+	u16 vertical_frame_num = 1;
+	u16 horizontal_frame_num = 1;
+	u16 min_first_frame = 0;
+	u16 max_first_frame = 0;
+	float frame_length = -1;
+	bool loop_animation = true;
+	u8 glow = 0;
 
 	*pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
 		>> minacc >> maxacc >> minexptime >> maxexptime >> minsize
@@ -948,29 +980,46 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
 		*pkt >> vertical;
 		*pkt >> collision_removal;
 		*pkt >> attached_id;
-
+		*pkt >> material_type_param;
+		*pkt >> animation_type;
+		*pkt >> vertical_frame_num;
+		*pkt >> horizontal_frame_num;
+		*pkt >> min_first_frame;
+		*pkt >> max_first_frame;
+		*pkt >> frame_length;
+		*pkt >> loop_animation;
+		*pkt >> glow;
 	} catch (...) {}
 
 	ClientEvent event;
-	event.type                                   = CE_ADD_PARTICLESPAWNER;
-	event.add_particlespawner.amount             = amount;
-	event.add_particlespawner.spawntime          = spawntime;
-	event.add_particlespawner.minpos             = new v3f (minpos);
-	event.add_particlespawner.maxpos             = new v3f (maxpos);
-	event.add_particlespawner.minvel             = new v3f (minvel);
-	event.add_particlespawner.maxvel             = new v3f (maxvel);
-	event.add_particlespawner.minacc             = new v3f (minacc);
-	event.add_particlespawner.maxacc             = new v3f (maxacc);
-	event.add_particlespawner.minexptime         = minexptime;
-	event.add_particlespawner.maxexptime         = maxexptime;
-	event.add_particlespawner.minsize            = minsize;
-	event.add_particlespawner.maxsize            = maxsize;
-	event.add_particlespawner.collisiondetection = collisiondetection;
-	event.add_particlespawner.collision_removal  = collision_removal;
-	event.add_particlespawner.attached_id        = attached_id;
-	event.add_particlespawner.vertical           = vertical;
-	event.add_particlespawner.texture            = new std::string(texture);
-	event.add_particlespawner.id                 = id;
+	event.type                                     = CE_ADD_PARTICLESPAWNER;
+	event.add_particlespawner.amount               = amount;
+	event.add_particlespawner.spawntime            = spawntime;
+	event.add_particlespawner.minpos               = new v3f (minpos);
+	event.add_particlespawner.maxpos               = new v3f (maxpos);
+	event.add_particlespawner.minvel               = new v3f (minvel);
+	event.add_particlespawner.maxvel               = new v3f (maxvel);
+	event.add_particlespawner.minacc               = new v3f (minacc);
+	event.add_particlespawner.maxacc               = new v3f (maxacc);
+	event.add_particlespawner.minexptime           = minexptime;
+	event.add_particlespawner.maxexptime           = maxexptime;
+	event.add_particlespawner.minsize              = minsize;
+	event.add_particlespawner.maxsize              = maxsize;
+	event.add_particlespawner.collisiondetection   = collisiondetection;
+	event.add_particlespawner.collision_removal    = collision_removal;
+	event.add_particlespawner.attached_id          = attached_id;
+	event.add_particlespawner.vertical             = vertical;
+	event.add_particlespawner.texture              = new std::string(texture);
+	event.add_particlespawner.id                   = id;
+	event.add_particlespawner.material_type_param  = material_type_param;
+	event.add_particlespawner.animation_type       = (AnimationType)animation_type;
+	event.add_particlespawner.vertical_frame_num   = vertical_frame_num;
+	event.add_particlespawner.horizontal_frame_num = horizontal_frame_num;
+	event.add_particlespawner.min_first_frame      = min_first_frame;
+	event.add_particlespawner.max_first_frame      = max_first_frame;
+	event.add_particlespawner.frame_length	       = frame_length;
+	event.add_particlespawner.loop_animation       = loop_animation;
+	event.add_particlespawner.glow                 = glow;
 
 	m_client_event_queue.push(event);
 }
diff --git a/src/nodedef.cpp b/src/nodedef.cpp
index 39ea1a60e2b6734e20233b9067584aff442a1a58..c690e6720de23addd2e2ca5a2190129018612d04 100644
--- a/src/nodedef.cpp
+++ b/src/nodedef.cpp
@@ -211,7 +211,7 @@ void TileDef::deSerialize(std::istream &is, const u8 contenfeatures_version, con
 {
 	int version = readU8(is);
 	name = deSerializeString(is);
-	animation.type = (TileAnimationType)readU8(is);
+	animation.type = (AnimationType)readU8(is);
 	animation.aspect_w = readU16(is);
 	animation.aspect_h = readU16(is);
 	animation.length = readF1000(is);
@@ -531,7 +531,7 @@ void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
 	tile->material_flags = 0;
 	if (backface_culling)
 		tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
-	if (tiledef->animation.type == TAT_VERTICAL_FRAMES)
+	if (tiledef->animation.type == AT_VERTICAL_FRAMES)
 		tile->material_flags |= MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES;
 	if (tiledef->tileable_horizontal)
 		tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
diff --git a/src/nodedef.h b/src/nodedef.h
index 80396f992308db095f40871d6ec5a4723286692f..f47517c4a46c402dbef7b7e153d80003956e40a4 100644
--- a/src/nodedef.h
+++ b/src/nodedef.h
@@ -161,9 +161,10 @@ enum NodeDrawType
 /*
 	Stand-alone definition of a TileSpec (basically a server-side TileSpec)
 */
-enum TileAnimationType{
-	TAT_NONE=0,
-	TAT_VERTICAL_FRAMES=1,
+enum AnimationType{
+	AT_NONE = 0,
+	AT_VERTICAL_FRAMES = 1,
+	AT_2D_ANIMATION_SHEET = 2,
 };
 struct TileDef
 {
@@ -172,7 +173,7 @@ struct TileDef
 	bool tileable_horizontal;
 	bool tileable_vertical;
 	struct{
-		enum TileAnimationType type;
+		enum AnimationType type;
 		int aspect_w; // width for aspect ratio
 		int aspect_h; // height for aspect ratio
 		float length; // seconds
@@ -184,7 +185,7 @@ struct TileDef
 		backface_culling = true;
 		tileable_horizontal = true;
 		tileable_vertical = true;
-		animation.type = TAT_NONE;
+		animation.type = AT_NONE;
 		animation.aspect_w = 1;
 		animation.aspect_h = 1;
 		animation.length = 1.0;
diff --git a/src/particles.cpp b/src/particles.cpp
index f20fb4083033631a48370ace89c6fc34a57be062..538487028e990de0367136b24bc52eea90d6e19b 100644
--- a/src/particles.cpp
+++ b/src/particles.cpp
@@ -43,6 +43,22 @@ v3f random_v3f(v3f min, v3f max)
 			rand()/(float)RAND_MAX*(max.Z-min.Z)+min.Z);
 }
 
+u32 check_material_type_param(u32 material_type_param)
+{
+	u32 alphaSource = (material_type_param & 0x0000F000) >> 12;
+	u32 modulo  = (material_type_param & 0x00000F00) >> 8;
+	u32 srcFact = (material_type_param & 0x000000F0) >> 4;
+	u32 dstFact = material_type_param & 0x0000000F;
+	if (alphaSource <= 3 && modulo <= 4 && srcFact <= 10 && dstFact <= 10) {
+		return material_type_param;
+	} else {
+		errorstream << "Server send incorrect ";
+		errorstream << "material_type_param value for particle.";
+		errorstream << std::endl;
+		return 0;
+	}
+}
+
 Particle::Particle(
 	IGameDef *gamedef,
 	scene::ISceneManager* smgr,
@@ -58,7 +74,14 @@ Particle::Particle(
 	bool vertical,
 	video::ITexture *texture,
 	v2f texpos,
-	v2f texsize
+	v2f texsize,
+	u32 material_type_param,
+	u16 vertical_frame_num,
+	u16 horizontal_frame_num,
+	u16 first_frame,
+	float frame_length,
+	bool loop_animation,
+	u8 glow
 ):
 	scene::ISceneNode(smgr->getRootSceneNode(), smgr)
 {
@@ -71,11 +94,26 @@ Particle::Particle(
 	m_material.setFlag(video::EMF_BACK_FACE_CULLING, false);
 	m_material.setFlag(video::EMF_BILINEAR_FILTER, false);
 	m_material.setFlag(video::EMF_FOG_ENABLE, true);
-	m_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+	if (material_type_param != 0) {
+		m_material.MaterialType = video::EMT_ONETEXTURE_BLEND;
+		m_material.MaterialTypeParam = irr::core::FR(material_type_param);
+		// We must disable z-buffer if we want to avoid transparent pixels
+		// to overlap pixels with lower z-value.
+		m_material.setFlag(video::EMF_ZWRITE_ENABLE, false); 
+	} else {
+		m_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+	}
 	m_material.setTexture(0, texture);
+
 	m_texpos = texpos;
 	m_texsize = texsize;
-
+	m_vertical_frame_num = vertical_frame_num;
+	m_horizontal_frame_num = horizontal_frame_num;
+	m_first_frame = first_frame;
+	m_frame_length = frame_length;
+	m_loop_animation = loop_animation;
+	m_texsize.Y /= m_vertical_frame_num;
+	m_texsize.X /= m_horizontal_frame_num;
 
 	// Particle related
 	m_pos = pos;
@@ -88,6 +126,7 @@ Particle::Particle(
 	m_collisiondetection = collisiondetection;
 	m_collision_removal = collision_removal;
 	m_vertical = vertical;
+	m_glow = glow;
 
 	// Irrlicht stuff
 	m_collisionbox = aabb3f
@@ -170,16 +209,29 @@ void Particle::updateLight()
 	else
 		light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0);
 
-	m_light = decode_light(light);
+	m_light = decode_light(light + m_glow);
 }
 
 void Particle::updateVertices()
 {
 	video::SColor c(255, m_light, m_light, m_light);
-	f32 tx0 = m_texpos.X;
-	f32 tx1 = m_texpos.X + m_texsize.X;
-	f32 ty0 = m_texpos.Y;
-	f32 ty1 = m_texpos.Y + m_texsize.Y;
+	u16 frame = m_first_frame;
+	if (m_frame_length > 0) {
+		if (m_loop_animation)
+			frame = m_first_frame + (u32)(m_time / m_frame_length)
+					% (m_vertical_frame_num * 
+					m_horizontal_frame_num - m_first_frame);
+		else if (m_time >= 
+				(m_vertical_frame_num * m_horizontal_frame_num 
+				- m_first_frame) * m_frame_length)
+			frame = m_vertical_frame_num * m_horizontal_frame_num - 1;
+		else
+			frame = m_first_frame + (u16)(m_time / m_frame_length);
+	}
+	f32 tx0 = m_texpos.X + m_texsize.X * (frame % m_horizontal_frame_num);
+	f32 tx1 = m_texpos.X + m_texsize.X * (frame % m_horizontal_frame_num + 1);
+	f32 ty0 = m_texpos.Y + m_texsize.Y * (frame / m_horizontal_frame_num);
+	f32 ty1 = m_texpos.Y + m_texsize.Y * (frame / m_horizontal_frame_num + 1);
 
 	m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0,
 			c, tx0, ty1);
@@ -214,7 +266,16 @@ ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr,
 	v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
 	float minexptime, float maxexptime, float minsize, float maxsize,
 	bool collisiondetection, bool collision_removal, u16 attached_id, bool vertical,
-	video::ITexture *texture, u32 id, ParticleManager *p_manager) :
+	video::ITexture *texture, u32 id, 
+	u32 material_type_param,
+	u16 vertical_frame_num,
+	u16 horizontal_frame_num,
+	u16 min_first_frame,
+	u16 max_first_frame,
+	float frame_length,
+	bool loop_animation,
+	u8 glow,
+	ParticleManager *p_manager) :
 	m_particlemanager(p_manager)
 {
 	m_gamedef = gamedef;
@@ -238,6 +299,14 @@ ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr,
 	m_vertical = vertical;
 	m_texture = texture;
 	m_time = 0;
+	m_vertical_frame_num = vertical_frame_num;
+	m_horizontal_frame_num = horizontal_frame_num;
+	m_min_first_frame = min_first_frame;
+	m_max_first_frame = max_first_frame;
+	m_frame_length = frame_length;
+	m_loop_animation = loop_animation;
+	m_material_type_param = material_type_param;
+	m_glow = glow;
 
 	for (u16 i = 0; i<=m_amount; i++)
 	{
@@ -251,7 +320,6 @@ ParticleSpawner::~ParticleSpawner() {}
 void ParticleSpawner::step(float dtime, ClientEnvironment* env)
 {
 	m_time += dtime;
-
 	bool unloaded = false;
 	v3f attached_offset = v3f(0,0,0);
 	if (m_attached_id != 0) {
@@ -285,7 +353,10 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env)
 					float size = rand()/(float)RAND_MAX
 							*(m_maxsize-m_minsize)
 							+m_minsize;
-
+					u16 first_frame = m_min_first_frame + 
+							rand() % 
+							(m_max_first_frame - 
+							m_min_first_frame + 1);
 					Particle* toadd = new Particle(
 						m_gamedef,
 						m_smgr,
@@ -301,7 +372,14 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env)
 						m_vertical,
 						m_texture,
 						v2f(0.0, 0.0),
-						v2f(1.0, 1.0));
+						v2f(1.0, 1.0),
+						m_material_type_param, 
+						m_vertical_frame_num,
+						m_horizontal_frame_num,
+						first_frame,
+						m_frame_length,
+						m_loop_animation,
+						m_glow);
 					m_particlemanager->addParticle(toadd);
 				}
 				i = m_spawntimes.erase(i);
@@ -331,7 +409,10 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env)
 				float size = rand()/(float)RAND_MAX
 						*(m_maxsize-m_minsize)
 						+m_minsize;
-
+				u16 first_frame = m_min_first_frame + 
+						rand() % 
+						(m_max_first_frame - 
+						m_min_first_frame + 1);
 				Particle* toadd = new Particle(
 					m_gamedef,
 					m_smgr,
@@ -347,7 +428,14 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env)
 					m_vertical,
 					m_texture,
 					v2f(0.0, 0.0),
-					v2f(1.0, 1.0));
+					v2f(1.0, 1.0),
+					m_material_type_param, 
+					m_vertical_frame_num,
+					m_horizontal_frame_num,
+					first_frame,
+					m_frame_length,
+					m_loop_animation,
+					m_glow);
 				m_particlemanager->addParticle(toadd);
 			}
 		}
@@ -459,6 +547,39 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef,
 			video::ITexture *texture =
 				gamedef->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture));
 
+			float frame_length = -1;
+			u16 vertical_frame_num = 1;
+			u16 horizontal_frame_num = 1;
+			u32 material_type_param =
+				check_material_type_param(event->add_particlespawner.material_type_param);
+
+			switch (event->add_particlespawner.animation_type) {
+				case AT_NONE:
+					break;
+				case AT_VERTICAL_FRAMES: {
+					v2u32 size = texture->getOriginalSize();
+					int frame_height = (float)size.X /
+						(float)event->add_particlespawner.vertical_frame_num *
+						(float)event->add_particlespawner.horizontal_frame_num;
+					vertical_frame_num = size.Y / frame_height;
+					frame_length = 
+						event->add_particlespawner.frame_length / 
+						vertical_frame_num;
+					break;
+				}
+				case AT_2D_ANIMATION_SHEET: {
+					vertical_frame_num =
+						event->add_particlespawner.vertical_frame_num;
+					horizontal_frame_num =
+						event->add_particlespawner.horizontal_frame_num;
+					frame_length = 
+						event->add_particlespawner.frame_length;
+					break;
+				}
+				default:
+					break;
+			}
+
 			ParticleSpawner* toadd = new ParticleSpawner(gamedef, smgr, player,
 					event->add_particlespawner.amount,
 					event->add_particlespawner.spawntime,
@@ -478,6 +599,14 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef,
 					event->add_particlespawner.vertical,
 					texture,
 					event->add_particlespawner.id,
+					material_type_param,
+					vertical_frame_num,
+					horizontal_frame_num,
+					event->add_particlespawner.min_first_frame,
+					event->add_particlespawner.max_first_frame,
+					frame_length,
+					event->add_particlespawner.loop_animation,
+					event->add_particlespawner.glow,
 					this);
 
 			/* delete allocated content of event */
@@ -502,6 +631,39 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef,
 			video::ITexture *texture =
 				gamedef->tsrc()->getTextureForMesh(*(event->spawn_particle.texture));
 
+			float frame_length = -1;
+			u16 vertical_frame_num = 1;
+			u16 horizontal_frame_num = 1;
+			u32 material_type_param =
+				check_material_type_param(event->spawn_particle.material_type_param);
+
+			switch (event->spawn_particle.animation_type) {
+				case AT_NONE:
+					break;
+				case AT_VERTICAL_FRAMES: {
+					v2u32 size = texture->getOriginalSize();
+					int frame_height = (float)size.X /
+						(float)event->spawn_particle.vertical_frame_num *
+						(float)event->spawn_particle.horizontal_frame_num;
+					vertical_frame_num = size.Y / frame_height;
+					frame_length = 
+						event->spawn_particle.frame_length / 
+						vertical_frame_num;
+					break;
+				}
+				case AT_2D_ANIMATION_SHEET: {
+					vertical_frame_num =
+						event->spawn_particle.vertical_frame_num;
+					horizontal_frame_num =
+						event->spawn_particle.horizontal_frame_num;
+					frame_length = 
+						event->spawn_particle.frame_length;
+					break;
+				}
+				default:
+					break;
+			}
+
 			Particle* toadd = new Particle(gamedef, smgr, player, m_env,
 					*event->spawn_particle.pos,
 					*event->spawn_particle.vel,
@@ -513,13 +675,21 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef,
 					event->spawn_particle.vertical,
 					texture,
 					v2f(0.0, 0.0),
-					v2f(1.0, 1.0));
+					v2f(1.0, 1.0),
+					material_type_param,
+					vertical_frame_num,
+					horizontal_frame_num,
+					event->spawn_particle.first_frame,
+					frame_length,
+					event->spawn_particle.loop_animation,
+					event->spawn_particle.glow);
 
 			addParticle(toadd);
 
 			delete event->spawn_particle.pos;
 			delete event->spawn_particle.vel;
 			delete event->spawn_particle.acc;
+			delete event->spawn_particle.texture;
 
 			break;
 		}
@@ -588,7 +758,8 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* s
 		false,
 		texture,
 		texpos,
-		texsize);
+		texsize,
+		0, 1, 1, 0, -1, true, 0);
 
 	addParticle(toadd);
 }
diff --git a/src/particles.h b/src/particles.h
index eb8c6665d0e127200e9dd1ddc5655f13e6494a23..6d8c6139f2debc8ffb8707421039551a5061dc80 100644
--- a/src/particles.h
+++ b/src/particles.h
@@ -50,7 +50,14 @@ class Particle : public scene::ISceneNode
 		bool vertical,
 		video::ITexture *texture,
 		v2f texpos,
-		v2f texsize
+		v2f texsize,
+		u32 material_type_param,
+		u16 vertical_frame_num,
+		u16 horizontal_frame_num,
+		u16 first_frame,
+		float frame_length,
+		bool loop_animation,
+		u8 glow
 	);
 	~Particle();
 
@@ -102,6 +109,12 @@ class Particle : public scene::ISceneNode
 	bool m_collision_removal;
 	bool m_vertical;
 	v3s16 m_camera_offset;
+	u16 m_vertical_frame_num;
+	u16 m_horizontal_frame_num;
+	u16 m_first_frame;
+	float m_frame_length;
+	bool m_loop_animation;
+	u8 m_glow;
 };
 
 class ParticleSpawner
@@ -123,8 +136,15 @@ class ParticleSpawner
 		bool vertical,
 		video::ITexture *texture,
 		u32 id,
+		u32 material_type_param,
+		u16 vertical_frame_num,
+		u16 horizontal_frame_num,
+		u16 min_first_frame,
+		u16 max_first_frame,
+		float frame_length,
+		bool loop_animation,
+		u8 glow,
 		ParticleManager* p_manager);
-
 	~ParticleSpawner();
 
 	void step(float dtime, ClientEnvironment *env);
@@ -156,6 +176,14 @@ class ParticleSpawner
 	bool m_collision_removal;
 	bool m_vertical;
 	u16 m_attached_id;
+	u32 m_material_type_param;
+	u16 m_vertical_frame_num;
+	u16 m_horizontal_frame_num;
+	u16 m_min_first_frame;
+	u16 m_max_first_frame;
+	float m_frame_length;
+	bool m_loop_animation;
+	u8 m_glow;
 };
 
 /**
diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp
index f20a65903c5015f1d40cd7a129ea773e1e02ef6f..d4a25b68b82e950682a741e0d9cd761fa9bcc62a 100644
--- a/src/script/common/c_content.cpp
+++ b/src/script/common/c_content.cpp
@@ -35,10 +35,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "noise.h"
 #include <json/json.h>
 
-struct EnumString es_TileAnimationType[] =
+struct EnumString es_AnimationType[] =
 {
-	{TAT_NONE, "none"},
-	{TAT_VERTICAL_FRAMES, "vertical_frames"},
+	{AT_NONE, "none"},
+	{AT_VERTICAL_FRAMES, "vertical_frames"},
+	{AT_2D_ANIMATION_SHEET, "2d_animation_sheet"},
 	{0, NULL},
 };
 
@@ -335,9 +336,9 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype)
 		lua_getfield(L, index, "animation");
 		if(lua_istable(L, -1)){
 			// {type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0}
-			tiledef.animation.type = (TileAnimationType)
-				getenumfield(L, -1, "type", es_TileAnimationType,
-				TAT_NONE);
+			tiledef.animation.type = (AnimationType)
+				getenumfield(L, -1, "type", es_AnimationType,
+				AT_NONE);
 			tiledef.animation.aspect_w =
 				getintfield_default(L, -1, "aspect_w", 16);
 			tiledef.animation.aspect_h =
diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h
index 2a2228b6dd2fb3922666768dab41b5aa6ab0c7f6..32fdb4f043ffac05667025d2b872cd589cc5d319 100644
--- a/src/script/common/c_content.h
+++ b/src/script/common/c_content.h
@@ -159,6 +159,6 @@ bool               push_json_value           (lua_State *L,
 void               read_json_value           (lua_State *L, Json::Value &root,
                                               int index, u8 recursion = 0);
 
-extern struct EnumString es_TileAnimationType[];
+extern struct EnumString es_AnimationType[];
 
 #endif /* C_CONTENT_H_ */
diff --git a/src/script/common/c_converter.cpp b/src/script/common/c_converter.cpp
index f36298915154b152975faca8416aa688533afccd..cfb5e26dbc7bf9731066e93e9359856fa6ab2beb 100644
--- a/src/script/common/c_converter.cpp
+++ b/src/script/common/c_converter.cpp
@@ -513,6 +513,28 @@ int getintfield_default(lua_State *L, int table,
 	return result;
 }
 
+int check_material_type_param(lua_State *L, int table,
+		const char *fieldname, int default_)
+{
+	int material_type_param =
+		getintfield_default(L, table, fieldname, default_);
+	u32 alphaSource = (material_type_param & 0x0000F000) >> 12;
+	u32 modulo  = (material_type_param & 0x00000F00) >> 8;
+	u32 srcFact = (material_type_param & 0x000000F0) >> 4;
+	u32 dstFact = material_type_param & 0x0000000F;
+	if (alphaSource <= 3 && modulo <= 4 && srcFact <= 10 && dstFact <= 10) {
+		return material_type_param;
+	} else {
+		std::ostringstream error_text;
+		error_text << "Incorrect material_type_param value ";
+		error_text << "for particle or particle spawner.";
+		error_text << std::endl;
+		throw LuaError(error_text.str());
+		return 0;
+	}
+}
+
+
 float getfloatfield_default(lua_State *L, int table,
 		const char *fieldname, float default_)
 {
diff --git a/src/script/common/c_converter.h b/src/script/common/c_converter.h
index a5fbee765ffd73d9d5163f8c6374e86e57e381c5..71ac735c15d9285d09a05dd44a1f80aafa62e3fb 100644
--- a/src/script/common/c_converter.h
+++ b/src/script/common/c_converter.h
@@ -45,6 +45,8 @@ float              getfloatfield_default(lua_State *L, int table,
                              const char *fieldname, float default_);
 int                getintfield_default           (lua_State *L, int table,
                              const char *fieldname, int default_);
+int                check_material_type_param(lua_State *L, int table,
+                             const char *fieldname, int default_);
 
 bool               getstringfield(lua_State *L, int table,
                              const char *fieldname, std::string &result);
diff --git a/src/script/lua_api/l_particles.cpp b/src/script/lua_api/l_particles.cpp
index 667ac727210b0d80fe36bb7dffd7e82031c5f5d5..b0a57ce6d3fe1778f543c44e27e9c80d26c6d57d 100644
--- a/src/script/lua_api/l_particles.cpp
+++ b/src/script/lua_api/l_particles.cpp
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "lua_api/l_object.h"
 #include "lua_api/l_internal.h"
 #include "common/c_converter.h"
+#include "common/c_content.h"
 #include "server.h"
 #include "particles.h"
 
@@ -34,6 +35,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 // collision_removal = bool
 // vertical = bool
 // texture = e.g."default_wood.png"
+// material_type_param = num
+// animation = animation definition
+// glow = indexed color or color string
 int ModApiParticles::l_add_particle(lua_State *L)
 {
 	MAP_LOCK_REQUIRED;
@@ -44,13 +48,24 @@ int ModApiParticles::l_add_particle(lua_State *L)
 
 	float expirationtime, size;
 	expirationtime = size = 1;
+	float frame_or_loop_length = -1;
+	
+	AnimationType animation_type = AT_NONE;
+
+	u16 vertical_frame_num_or_aspect = 1;
+	u16 horizontal_frame_num_or_aspect = 1;
+	u16 first_frame = 0;
 
 	bool collisiondetection, vertical, collision_removal;
 	collisiondetection = vertical = collision_removal = false;
+	bool loop_animation = true;
 
 	std::string texture = "";
 	std::string playername = "";
 
+	u32 material_type_param = 0;
+	u8 glow = 0;
+
 	if (lua_gettop(L) > 1) // deprecated
 	{
 		log_deprecated(L, "Deprecated add_particle call with individual parameters instead of definition");
@@ -94,8 +109,61 @@ int ModApiParticles::l_add_particle(lua_State *L)
 		acc = lua_istable(L, -1) ? check_v3f(L, -1) : acc;
 		lua_pop(L, 1);
 
-		expirationtime = getfloatfield_default(L, 1, "expirationtime", 1);
+		expirationtime = getfloatfield_default(L, 1, "expirationtime", 1);	
 		size = getfloatfield_default(L, 1, "size", 1);
+
+		lua_getfield(L, 1, "animation");
+		if (lua_istable(L, -1)) {
+			animation_type = (AnimationType)
+				getenumfield(L, -1, "type", es_AnimationType,
+				AT_NONE);
+		}
+		switch (animation_type) {
+			case AT_NONE:
+				break;
+			case AT_2D_ANIMATION_SHEET:
+				frame_or_loop_length = 
+					getfloatfield_default(L, -1, "frame_length", -1);
+				vertical_frame_num_or_aspect = 
+					getintfield_default(L, -1, "vertical_frame_num", 1);
+				horizontal_frame_num_or_aspect = 
+					getintfield_default(L, -1, "horizontal_frame_num", 1);
+				first_frame = 
+					getintfield_default(L, -1, "first_frame", 0);
+				loop_animation = 
+					getboolfield_default(L, -1, "loop_animation", true);
+				break;
+			case AT_VERTICAL_FRAMES:
+				frame_or_loop_length = 
+					getfloatfield_default(L, -1, "length", -1);
+				vertical_frame_num_or_aspect = 
+					getintfield_default(L, -1, "aspect_w", 1);
+				horizontal_frame_num_or_aspect = 
+					getintfield_default(L, -1, "aspect_h", 1);
+				first_frame = 
+					getintfield_default(L, -1, "first_frame", 0);
+				loop_animation = 
+					getboolfield_default(L, -1, "loop_animation", true);
+				break;
+			default:
+				break;
+		}
+		lua_pop(L, 1);
+
+		if (animation_type == AT_2D_ANIMATION_SHEET && 
+				first_frame >= vertical_frame_num_or_aspect * 
+				horizontal_frame_num_or_aspect) {
+			std::ostringstream error_text; 
+			error_text << "first_frame should be lower, than "
+				<< "vertical_frame_num * horizontal_frame_num. "
+				<< "Got first_frame=" << first_frame
+				<< ", vertical_frame_num="
+				<< vertical_frame_num_or_aspect
+				<< " and horizontal_frame_num="
+				<< horizontal_frame_num_or_aspect << std::endl;
+			throw LuaError(error_text.str());
+		}
+
 		collisiondetection = getboolfield_default(L, 1,
 			"collisiondetection", collisiondetection);
 		collision_removal = getboolfield_default(L, 1,
@@ -103,9 +171,16 @@ int ModApiParticles::l_add_particle(lua_State *L)
 		vertical = getboolfield_default(L, 1, "vertical", vertical);
 		texture = getstringfield_default(L, 1, "texture", "");
 		playername = getstringfield_default(L, 1, "playername", "");
+		material_type_param = check_material_type_param(L, 1, "material_type_param", 0);
+		glow = getintfield_default (L, 1, "glow", 0);
 	}
-	getServer(L)->spawnParticle(playername, pos, vel, acc, expirationtime, size,
-			collisiondetection, collision_removal, vertical, texture);
+	getServer(L)->spawnParticle(playername, pos, vel, acc, expirationtime, 
+		size, collisiondetection, collision_removal, vertical, 
+		texture, material_type_param,
+		animation_type,
+		vertical_frame_num_or_aspect, 
+		horizontal_frame_num_or_aspect,
+		first_frame, frame_or_loop_length, loop_animation, glow);
 	return 1;
 }
 
@@ -127,21 +202,33 @@ int ModApiParticles::l_add_particle(lua_State *L)
 // collision_removal = bool
 // vertical = bool
 // texture = e.g."default_wood.png"
+// material_type_param = num
+// animation = animation definition
+// glow = indexed color or color string
 int ModApiParticles::l_add_particlespawner(lua_State *L)
 {
 	MAP_LOCK_REQUIRED;
 
 	// Get parameters
 	u16 amount = 1;
+	u16 vertical_frame_num_or_aspect = 1;
+	u16 horizontal_frame_num_or_aspect = 1;
+	u16 min_first_frame = 0;
+	u16 max_first_frame = 0;
 	v3f minpos, maxpos, minvel, maxvel, minacc, maxacc;
 	    minpos= maxpos= minvel= maxvel= minacc= maxacc= v3f(0, 0, 0);
 	float time, minexptime, maxexptime, minsize, maxsize;
 	      time= minexptime= maxexptime= minsize= maxsize= 1;
+	AnimationType animation_type = AT_NONE;
+	float frame_or_loop_length = -1;
 	bool collisiondetection, vertical, collision_removal;
 	     collisiondetection = vertical = collision_removal = false;
+	bool loop_animation = true;
 	ServerActiveObject *attached = NULL;
 	std::string texture = "";
 	std::string playername = "";
+	u32 material_type_param = 0;
+	u8 glow = 0;
 
 	if (lua_gettop(L) > 1) //deprecated
 	{
@@ -196,6 +283,65 @@ int ModApiParticles::l_add_particlespawner(lua_State *L)
 		maxexptime = getfloatfield_default(L, 1, "maxexptime", maxexptime);
 		minsize = getfloatfield_default(L, 1, "minsize", minsize);
 		maxsize = getfloatfield_default(L, 1, "maxsize", maxsize);
+
+
+		lua_getfield(L, 1, "animation");
+		if (lua_istable(L, -1)) {
+			animation_type = (AnimationType)
+				getenumfield(L, -1, "type", es_AnimationType,
+				AT_NONE);
+		}
+		switch (animation_type) {
+			case AT_NONE:
+				break;
+			case AT_2D_ANIMATION_SHEET:
+				frame_or_loop_length = 
+					getfloatfield_default(L, -1, "frame_length", -1);
+				vertical_frame_num_or_aspect = 
+					getintfield_default(L, -1, "vertical_frame_num", 1);
+				horizontal_frame_num_or_aspect = 
+					getintfield_default(L, -1, "horizontal_frame_num", 1);
+				min_first_frame = 
+					getintfield_default(L, -1, "min_first_frame", 0);
+				max_first_frame = 
+					getintfield_default(L, -1, "max_first_frame", 0);
+				loop_animation = 
+					getboolfield_default(L, -1, "loop_animation", true);
+				break;
+			case AT_VERTICAL_FRAMES:
+				frame_or_loop_length = 
+					getfloatfield_default(L, -1, "length", -1);
+				vertical_frame_num_or_aspect = 
+					getintfield_default(L, -1, "aspect_w", 1);
+				horizontal_frame_num_or_aspect = 
+					getintfield_default(L, -1, "aspect_h", 1);
+				min_first_frame = 
+					getintfield_default(L, -1, "min_first_frame", 0);
+				max_first_frame = 
+					getintfield_default(L, -1, "max_first_frame", 0);
+				loop_animation = 
+					getboolfield_default(L, -1, "loop_animation", true);
+				break;
+			default:
+				break;
+		}
+		lua_pop(L, 1);
+
+		if (animation_type == AT_2D_ANIMATION_SHEET && 
+				max_first_frame >= vertical_frame_num_or_aspect * 
+				horizontal_frame_num_or_aspect) {
+			std::ostringstream error_text; 
+			error_text << "max_first_frame should be lower, than "
+				<< "vertical_frame_num * horizontal_frame_num. " 
+				<< "Got max_first_frame="
+				<< max_first_frame
+				<< ", vertical_frame_num="
+				<< vertical_frame_num_or_aspect
+				<< " and horizontal_frame_num="
+				<< horizontal_frame_num_or_aspect << std::endl;
+			throw LuaError(error_text.str());
+		}
+		
 		collisiondetection = getboolfield_default(L, 1,
 			"collisiondetection", collisiondetection);
 		collision_removal = getboolfield_default(L, 1,
@@ -211,6 +357,8 @@ int ModApiParticles::l_add_particlespawner(lua_State *L)
 		vertical = getboolfield_default(L, 1, "vertical", vertical);
 		texture = getstringfield_default(L, 1, "texture", "");
 		playername = getstringfield_default(L, 1, "playername", "");
+		material_type_param = check_material_type_param(L, 1, "material_type_param", 0);
+		glow = getintfield_default(L, 1, "glow", 0);
 	}
 
 	u32 id = getServer(L)->addParticleSpawner(amount, time,
@@ -223,9 +371,17 @@ int ModApiParticles::l_add_particlespawner(lua_State *L)
 			collision_removal,
 			attached,
 			vertical,
-			texture, playername);
+			texture, 
+			playername, 
+			material_type_param, 
+			animation_type,
+			vertical_frame_num_or_aspect, 
+			horizontal_frame_num_or_aspect,
+			min_first_frame, max_first_frame, 
+			frame_or_loop_length, 
+			loop_animation,
+			glow);
 	lua_pushnumber(L, id);
-
 	return 1;
 }
 
diff --git a/src/server.cpp b/src/server.cpp
index 48331e4f84b2dad4eaf56c6815d6319468b224e9..cef57be888a5238debbacd2e727d01b95858aff0 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -1658,7 +1658,11 @@ void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec,
 void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration,
 				float expirationtime, float size, bool collisiondetection,
 				bool collision_removal,
-				bool vertical, const std::string &texture)
+				bool vertical, const std::string &texture,
+				u32 material_type_param,  AnimationType animation_type,
+				u16 vertical_frame_num, u16 horizontal_frame_num, u16 first_frame,
+				float frame_length, bool loop_animation, 
+				u8 glow)
 {
 	DSTACK(FUNCTION_NAME);
 
@@ -1670,6 +1674,12 @@ void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f accelerat
 	pkt << vertical;
 	pkt << collision_removal;
 
+	pkt << material_type_param 
+		<< (u8)animation_type
+		<< vertical_frame_num 
+		<< horizontal_frame_num << first_frame 
+		<< frame_length << loop_animation << glow;
+
 	if (peer_id != PEER_ID_INEXISTENT) {
 		Send(&pkt);
 	}
@@ -1682,7 +1692,10 @@ void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f accelerat
 void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos,
 	v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
 	float minsize, float maxsize, bool collisiondetection, bool collision_removal,
-	u16 attached_id, bool vertical, const std::string &texture, u32 id)
+	u16 attached_id, bool vertical, const std::string &texture, u32 id,
+	u32 material_type_param,  AnimationType animation_type, u16 vertical_frame_num, u16 horizontal_frame_num, 
+	u16 min_first_frame, u16 max_first_frame, float frame_length, 
+	bool loop_animation, u8 glow)
 {
 	DSTACK(FUNCTION_NAME);
 
@@ -1698,6 +1711,12 @@ void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3
 	pkt << collision_removal;
 	pkt << attached_id;
 
+	pkt << material_type_param 
+		<< (u8)animation_type
+		<< vertical_frame_num << horizontal_frame_num 
+		<< min_first_frame << max_first_frame
+		<< frame_length << loop_animation << glow;
+
 	if (peer_id != PEER_ID_INEXISTENT) {
 		Send(&pkt);
 	}
@@ -3147,7 +3166,11 @@ void Server::spawnParticle(const std::string &playername, v3f pos,
 	v3f velocity, v3f acceleration,
 	float expirationtime, float size, bool
 	collisiondetection, bool collision_removal,
-	bool vertical, const std::string &texture)
+	bool vertical, const std::string &texture,
+	u32 material_type_param,  AnimationType animation_type,
+	u16 vertical_frame_num, u16 horizontal_frame_num, u16 first_frame,
+	float frame_length, bool loop_animation,
+	u8 glow)
 {
 	// m_env will be NULL if the server is initializing
 	if (!m_env)
@@ -3163,7 +3186,11 @@ void Server::spawnParticle(const std::string &playername, v3f pos,
 
 	SendSpawnParticle(peer_id, pos, velocity, acceleration,
 			expirationtime, size, collisiondetection,
-			collision_removal, vertical, texture);
+			collision_removal, vertical, texture,
+			material_type_param, animation_type,
+			vertical_frame_num, horizontal_frame_num, 
+			first_frame, frame_length, loop_animation, 
+			glow);
 }
 
 u32 Server::addParticleSpawner(u16 amount, float spawntime,
@@ -3171,7 +3198,9 @@ u32 Server::addParticleSpawner(u16 amount, float spawntime,
 	float minexptime, float maxexptime, float minsize, float maxsize,
 	bool collisiondetection, bool collision_removal,
 	ServerActiveObject *attached, bool vertical, const std::string &texture,
-	const std::string &playername)
+	const std::string &playername,	u32 material_type_param,  AnimationType animation_type,
+	u16 vertical_frame_num, u16 horizontal_frame_num, u16 min_first_frame, u16 max_first_frame, 
+	float frame_length, bool loop_animation, u8 glow)
 {
 	// m_env will be NULL if the server is initializing
 	if (!m_env)
@@ -3197,7 +3226,10 @@ u32 Server::addParticleSpawner(u16 amount, float spawntime,
 		minpos, maxpos, minvel, maxvel, minacc, maxacc,
 		minexptime, maxexptime, minsize, maxsize,
 		collisiondetection, collision_removal, attached_id, vertical,
-		texture, id);
+		texture, id, material_type_param, animation_type,
+		vertical_frame_num, horizontal_frame_num, 
+		min_first_frame, max_first_frame, frame_length, loop_animation,
+		glow);
 
 	return id;
 }
diff --git a/src/server.h b/src/server.h
index 9e844e36c3531ccf641dbefef209c3bfc0d76c7c..9a8d22b2eb4cc42e309e6be44ef52db436486ad2 100644
--- a/src/server.h
+++ b/src/server.h
@@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "hud.h"
 #include "gamedef.h"
 #include "serialization.h" // For SER_FMT_VER_INVALID
+#include "nodedef.h" // AnimationType
 #include "mods.h"
 #include "inventorymanager.h"
 #include "subgame.h"
@@ -254,7 +255,11 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 		v3f pos, v3f velocity, v3f acceleration,
 		float expirationtime, float size,
 		bool collisiondetection, bool collision_removal,
-		bool vertical, const std::string &texture);
+		bool vertical, const std::string &texture,
+		u32 material_type_param,  AnimationType animation_type, 
+		u16 vertical_frame_num, u16 horizontal_frame_num, u16 first_frame,
+		float frame_length, bool loop_animation,
+		u8 glow);
 
 	u32 addParticleSpawner(u16 amount, float spawntime,
 		v3f minpos, v3f maxpos,
@@ -265,7 +270,12 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 		bool collisiondetection, bool collision_removal,
 		ServerActiveObject *attached,
 		bool vertical, const std::string &texture,
-		const std::string &playername);
+		const std::string &playername,
+		u32 material_type_param,  AnimationType animation_type, 
+		u16 vertical_frame_num, u16 horizontal_frame_num, 
+		u16 min_first_frame, u16 max_first_frame, 
+		float frame_length, bool loop_animation,
+		u8 glow);
 
 	void deleteParticleSpawner(const std::string &playername, u32 id);
 
@@ -441,7 +451,12 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 		float minsize, float maxsize,
 		bool collisiondetection, bool collision_removal,
 		u16 attached_id,
-		bool vertical, const std::string &texture, u32 id);
+		bool vertical, const std::string &texture, u32 id,
+		u32 material_type_param,  AnimationType animation_type, 
+		u16 vertical_frame_num, u16 horizontal_frame_num, 
+		u16 min_first_frame, u16 max_first_frame, 
+		float frame_length, bool loop_animation,
+		u8 glow);
 
 	void SendDeleteParticleSpawner(u16 peer_id, u32 id);
 
@@ -450,7 +465,11 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 		v3f pos, v3f velocity, v3f acceleration,
 		float expirationtime, float size,
 		bool collisiondetection, bool collision_removal,
-		bool vertical, const std::string &texture);
+		bool vertical, const std::string &texture,
+		u32 material_type_param,  AnimationType animation_type, 
+		u16 vertical_frame_num, u16 horizontal_frame_num, u16 first_frame,
+		float frame_length, bool loop_animation,
+		u8 glow);
 
 	u32 SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas);
 	void SendActiveObjectMessages(u16 peer_id, const std::string &datas, bool reliable = true);