diff --git a/game/engines/default/engine/interface/ActorTalents.lua b/game/engines/default/engine/interface/ActorTalents.lua
index c462735488d41c1b809bbc7703a4c25b079ea4a3..73343b2f6ffe10e9e37851bd9ee63aa497e41145 100644
--- a/game/engines/default/engine/interface/ActorTalents.lua
+++ b/game/engines/default/engine/interface/ActorTalents.lua
@@ -1006,7 +1006,7 @@ function _M:talentParticles(p, ...)
 	local Particles = require "engine.Particles"
 	if not p.__tmpparticles then p.__tmpparticles = {} end
 	for _, ps in ipairs{...} do
-		p.__tmpparticles[#p.__tmpparticles+1] = self:addParticles(Particles.new(ps.type, 1, ps.args))
+		p.__tmpparticles[#p.__tmpparticles+1] = self:addParticles(Particles.new(ps.type, 1, ps.args, ps.shader))
 	end
 end
 
diff --git a/game/engines/default/engine/interface/ActorTemporaryEffects.lua b/game/engines/default/engine/interface/ActorTemporaryEffects.lua
index df72b87cebd5ebd6ebfaf9fe627efb1a321d0211..e498a656d59dfffae2f956ef084310743344e294 100644
--- a/game/engines/default/engine/interface/ActorTemporaryEffects.lua
+++ b/game/engines/default/engine/interface/ActorTemporaryEffects.lua
@@ -275,7 +275,7 @@ function _M:effectParticles(eff, ...)
 	local Particles = require "engine.Particles"
 	if not eff.__tmpparticles then eff.__tmpparticles = {} end
 	for _, p in ipairs{...} do
-		eff.__tmpparticles[#eff.__tmpparticles+1] = self:addParticles(Particles.new(p.type, 1, p.args))
+		eff.__tmpparticles[#eff.__tmpparticles+1] = self:addParticles(Particles.new(p.type, 1, p.args, ps.shader))
 	end
 end
 
diff --git a/game/modules/tome/data/gfx/particles/shader_shield.lua b/game/modules/tome/data/gfx/particles/shader_shield.lua
index 34caccd3c4e3c627d28a3530f9f6d8900fe36281..774885aa214a66bc0146e59abe08d2f68e9d3f16 100644
--- a/game/modules/tome/data/gfx/particles/shader_shield.lua
+++ b/game/modules/tome/data/gfx/particles/shader_shield.lua
@@ -19,9 +19,9 @@
 
 base_size = 64
 
-local r = 1
-local g = 1
-local b = 1
+local r = r or 1
+local g = g or 1
+local b = b or 1
 local a = a or 1
 
 return { blend_mode = blend and core.particles.BLEND_SHINY or nil, generator = function()
diff --git a/game/modules/tome/data/gfx/particles_images/rotating_shield.png b/game/modules/tome/data/gfx/particles_images/rotating_shield.png
new file mode 100644
index 0000000000000000000000000000000000000000..5dc7f5f505161f8b88c318451062c5d3bff29785
Binary files /dev/null and b/game/modules/tome/data/gfx/particles_images/rotating_shield.png differ
diff --git a/game/modules/tome/data/gfx/shaders/rotatingshield.frag b/game/modules/tome/data/gfx/shaders/rotatingshield.frag
new file mode 100644
index 0000000000000000000000000000000000000000..fb87ae227a301acc61a2837b7bfa46bebbc85c33
--- /dev/null
+++ b/game/modules/tome/data/gfx/shaders/rotatingshield.frag
@@ -0,0 +1,86 @@
+uniform sampler2D tex;
+uniform float tick;
+uniform float tick_start;
+uniform float time_factor;
+uniform float noup;
+uniform float cylinderRotationSpeed; //rotation speed of the aura, min: 0, max: 10, def: 1
+uniform float cylinderRadius; //radius of the cylinder aura. min: 0.2, max: 0.5, def: 0.45
+uniform float cylinderVerticalPos; //vertical position of the cylinder. 0 is in the middle. min: -0.2, max: 0.2
+uniform float cylinderHeight; //height of the cylinder. min: 0.1, max: 1.0, default: 0.4
+uniform float appearTime; //normalized appearence time. min: 0.01, max: 3.0, default: 1.0f
+uniform float repeatTimes;
+uniform float unbalancedSize;
+
+vec2 Rotate(vec2 point, float ang)
+{
+  return vec2(
+    point.x * cos(ang) - point.y * sin(ang),
+    point.x * sin(ang) + point.y * cos(ang));
+}
+
+vec4 Uberblend(vec4 col0, vec4 col1)
+{
+//  return vec4((1.0 - col0.a) * (col1.rgb) + col0.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a));
+//  return vec4((1.0 - col1.a) * (col0.rgb) + col1.a * (col1.rgb * col1.a + col0.rgb * (1.0 - col1.a)), min(1.0, col0.a + col1.a));
+  return vec4(
+    (1.0 - col0.a) * (1.0 - col1.a) * (col0.rgb * col0.a + col1.rgb * col1.a) / (col0.a + col1.a + 1e-1) +
+    (1.0 - col0.a) * (0.0 + col1.a) * (col1.rgb) +
+    (0.0 + col0.a) * (1.0 - col1.a) * (col0.rgb * (1.0 - col1.a) + col1.rgb * col1.a) +
+    (0.0 + col0.a) * (0.0 + col1.a) * (col1.rgb),
+    min(1.0, col0.a + col1.a));
+}
+
+void main(void)
+{
+  vec2 pos = vec2(0.5, 0.5) - gl_TexCoord[0].xy;
+
+  vec4 resultColor = vec4(0.0, 0.0, 0.0, 0.0);
+
+  float dist = 2.0;
+  vec3 rayDir = normalize(vec3(0.0, -0.5, 1.0));
+  vec3 xVector = normalize(cross(vec3(0.0, 1.0, 0.0), rayDir));
+  vec3 yVector = cross(rayDir, xVector);
+
+  vec3 rayPoint = vec3(0.0) - rayDir * dist + xVector * pos.x + yVector * pos.y;
+
+  float normTime = (tick - tick_start) / time_factor;
+  float alpha = normTime * cylinderRotationSpeed;
+
+  if(abs(pos.x) < cylinderRadius)
+  {
+    float localWidth = sqrt(cylinderRadius * cylinderRadius - pos.x * pos.x);
+    vec3 startPoint = rayPoint + rayDir * (-localWidth - rayPoint.z) / rayDir.z;
+    vec3 endPoint = rayPoint + rayDir * ( localWidth - rayPoint.z) / rayDir.z;
+
+    vec3 cylinderPoint;
+    if(noup == 1.0)
+    {
+      cylinderPoint = startPoint;
+    }else
+    if(noup == 2.0)
+    {
+      cylinderPoint = endPoint;
+    }
+    float farRatio = (cylinderPoint.z - (-cylinderRadius)) / (2.0 * cylinderRadius);
+    float farDarkening = mix(1.0, 0.1, farRatio);
+
+    vec2 planarPos;
+    planarPos.y = (cylinderPoint.y + cylinderHeight * 0.5 + cylinderVerticalPos) / cylinderHeight;
+    planarPos.y = 1.0 - planarPos.y;
+    planarPos.y = 0.5 + (planarPos.y - 0.5) * (1.0 + cylinderPoint.z * 0.5);
+    planarPos.x = mod((atan(cylinderPoint.z, cylinderPoint.x) + alpha) / (2.0 * 3.1415), 1.0);
+
+    if(planarPos.y < 1.0 && planarPos.y > 0.0)
+    {
+      float glowMult = 1.0 + mix(3.0, 0.0, clamp(normTime / (appearTime * 1.5 + 1e-4), 0.0, 1.0));
+      float alphaSub = 1.0f - clamp(normTime / (appearTime + 1e-4), 0.0, 1.0);;
+      planarPos.x *= repeatTimes;
+      planarPos.x = clamp(planarPos.x, 0.0, unbalancedSize);
+      resultColor = texture2D(tex, planarPos);
+      resultColor.rgb *= farDarkening;
+      resultColor.rgb *= glowMult;
+      resultColor.a = (resultColor.a - alphaSub) / (1.0 - alphaSub + 1e-4);
+    }
+  }
+  gl_FragColor = resultColor * gl_Color;
+}
diff --git a/game/modules/tome/data/gfx/shaders/rotatingshield.lua b/game/modules/tome/data/gfx/shaders/rotatingshield.lua
new file mode 100644
index 0000000000000000000000000000000000000000..f5e4d0f3afabb5e8f112a0e88efb1124bcb0c32c
--- /dev/null
+++ b/game/modules/tome/data/gfx/shaders/rotatingshield.lua
@@ -0,0 +1,39 @@
+-- ToME - Tales of Maj'Eyal
+-- Copyright (C) 2009 - 2016 Nicolas Casalini
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-- Nicolas Casalini "DarkGod"
+-- darkgod@te4.org
+
+return {
+	frag = "rotatingshield",
+	vert = nil,
+	args = {
+		tex = { texture = 0 },
+		noup = noup or 0,
+		time_factor = time_factor or 1000,
+		cylinderRotationSpeed = cylinderRotationSpeed or 1,  -- rotation speed of the aura, min: 0, max: 10, def: 1
+		cylinderRadius = cylinderRadius or 0.45,  -- radius of the cylinder aura. min: 0.2, max: 0.5, def: 0.45
+		cylinderVerticalPos = cylinderVerticalPos or 0,  -- vertical position of the cylinder. 0 is in the middle. min: -0.2, max: 0.2
+		cylinderHeight = cylinderHeight or 0.4,  -- height of the cylinder. min: 0.1, max: 1.0, default: 0.4
+		appearTime = appearTime or 1,  -- normalized appearence time. min: 0.01, max: 3.0, default: 1.0f
+		repeatTimes = repeatTimes or 1,
+		unbalancedSize = unbalancedSize or 1,
+	},
+	resetargs = {
+		tick_start = function() return core.game.getFrameTime() end,
+	},
+	clone = false,
+}
diff --git a/game/modules/tome/data/talents/techniques/weaponshield.lua b/game/modules/tome/data/talents/techniques/weaponshield.lua
index 0714337dd3188714d7aa6a263ca38e4f38877502..78b468171bceac7b2455c7e3d702868b666056c5 100644
--- a/game/modules/tome/data/talents/techniques/weaponshield.lua
+++ b/game/modules/tome/data/talents/techniques/weaponshield.lua
@@ -204,13 +204,18 @@ newTalent{
 			game.logPlayer(self, "You cannot use Shield Wall without a shield!")
 			return nil
 		end
-		return {
+		local ret = {
 			stun = self:addTemporaryValue("stun_immune", t.stunKBresist(self, t)),
 			knock = self:addTemporaryValue("knockback_immune", t.stunKBresist(self, t)),
 			dam = self:addTemporaryValue("inc_damage", {[DamageType.PHYSICAL]=-20}),
 			def = self:addTemporaryValue("combat_def", t.getDefense(self, t)),
 			armor = self:addTemporaryValue("combat_armor", t.getarmor(self,t)),
 		}
+		if core.shader.active(4) then
+			self:talentParticles(ret, {type="shader_shield", args={toback=true,  size_factor=1, img="rotating_shield"}, shader={type="rotatingshield", noup=2.0, appearTime=0.2}})
+			self:talentParticles(ret, {type="shader_shield", args={toback=false, size_factor=1, img="rotating_shield"}, shader={type="rotatingshield", noup=1.0, appearTime=0.2}})
+		end
+		return ret
 	end,
 	deactivate = function(self, t, p)
 		self:removeTemporaryValue("combat_def", p.def)