diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java
index 4e50d13e..740a2a21 100644
--- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java
+++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java
@@ -13,6 +13,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
 import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
 import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
 import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect;
+import com.minelittlepony.unicopia.particle.ParticleHandle;
 import com.minelittlepony.unicopia.particle.ParticleUtils;
 import com.minelittlepony.unicopia.particle.SphereParticleEffect;
 import com.minelittlepony.unicopia.particle.UParticles;
@@ -51,6 +52,9 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
     private int age = 0;
     private float accumulatedMass = 0;
 
+    @Deprecated
+    protected final ParticleHandle particlEffect = new ParticleHandle();
+
     protected DarkVortexSpell(CustomisedSpellType<?> type) {
         super(type);
     }
@@ -109,6 +113,11 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
         return EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR.test(entity) && getAttractiveForce(source, entity) > 0;
     }
 
+    @Override
+    protected void onDestroyed(Caster<?> caster) {
+        particlEffect.destroy();
+    }
+
     @Override
     public void generateParticles(Caster<?> source) {
         super.generateParticles(source);
diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java
index 71f5cede..023da1c7 100644
--- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java
+++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java
@@ -1,5 +1,7 @@
 package com.minelittlepony.unicopia.ability.magic.spell.effect;
 
+import java.util.Optional;
+
 import com.minelittlepony.unicopia.Affinity;
 import com.minelittlepony.unicopia.USounds;
 import com.minelittlepony.unicopia.Unicopia;
@@ -10,11 +12,9 @@ import com.minelittlepony.unicopia.ability.magic.spell.Spell;
 import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
 import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
 import com.minelittlepony.unicopia.entity.player.Pony;
+import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect;
 import com.minelittlepony.unicopia.particle.MagicParticleEffect;
-import com.minelittlepony.unicopia.particle.ParticleHandle;
-import com.minelittlepony.unicopia.particle.SphereParticleEffect;
-import com.minelittlepony.unicopia.particle.UParticles;
-import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
+import com.minelittlepony.unicopia.particle.ParticleUtils;
 import com.minelittlepony.unicopia.projectile.ProjectileUtil;
 import com.minelittlepony.unicopia.util.shape.Sphere;
 
@@ -30,6 +30,7 @@ import net.minecraft.entity.passive.PassiveEntity;
 import net.minecraft.entity.player.PlayerEntity;
 import net.minecraft.entity.vehicle.AbstractMinecartEntity;
 import net.minecraft.entity.vehicle.BoatEntity;
+import net.minecraft.util.math.MathHelper;
 import net.minecraft.util.math.Vec3d;
 
 public class ShieldSpell extends AbstractSpell {
@@ -40,10 +41,14 @@ public class ShieldSpell extends AbstractSpell {
             .with(Trait.AIR, 9)
             .build();
 
-    protected final ParticleHandle particlEffect = new ParticleHandle();
-
     private final TargetSelecter targetSelecter = new TargetSelecter(this);
 
+    private float prevRadius;
+    private float radius;
+
+    private float rangeMultiplier;
+    private float targetRangeMultiplier;
+
     protected ShieldSpell(CustomisedSpellType<?> type) {
         super(type);
     }
@@ -53,33 +58,27 @@ public class ShieldSpell extends AbstractSpell {
         return method == CastingMethod.STAFF || getTraits().get(Trait.GENEROSITY) > 0 ? toPlaceable() : this;
     }
 
-    @Override
-    protected void onDestroyed(Caster<?> caster) {
-        particlEffect.destroy();
-    }
-
     @Override
     public Affinity getAffinity() {
         return getTraits().get(Trait.DARKNESS) > 0 ? Affinity.BAD : Affinity.GOOD;
     }
 
     protected void generateParticles(Caster<?> source) {
-        float radius = (float)getDrawDropOffRange(source);
         Vec3d origin = getOrigin(source);
 
         source.spawnParticles(origin, new Sphere(true, radius), (int)(radius * 6), pos -> {
             source.addParticle(new MagicParticleEffect(getType().getColor()), pos, Vec3d.ZERO);
-        });
 
-        particlEffect.update(getUuid(), source, spawner -> {
-            spawner.addParticle(new SphereParticleEffect(UParticles.SPHERE, getType().getColor(), 0.3F, radius), origin, Vec3d.ZERO);
-        }).ifPresent(p -> {
-            p.setAttribute(Attachment.ATTR_RADIUS, radius);
+            if (source.asWorld().random.nextInt(10) == 0 && source.asWorld().random.nextFloat() < source.getCorruption().getScaled(1)) {
+                ParticleUtils.spawnParticle(source.asWorld(), new LightningBoltParticleEffect(true, 3, 2, 0.1F, Optional.empty()), pos, Vec3d.ZERO);
+            }
         });
     }
 
     @Override
     public boolean tick(Caster<?> source, Situation situation) {
+        prevRadius = radius;
+        radius = (float)getDrawDropOffRange(source);
 
         if (source.isClient()) {
             generateParticles(source);
@@ -108,20 +107,32 @@ public class ShieldSpell extends AbstractSpell {
 
         cost *= costMultiplier / ((1 + source.getLevel().get()) * 3F);
         cost /= knowledge;
-        cost += getDrawDropOffRange(source) / 10F;
+        cost += radius / 10F;
 
         if (!source.subtractEnergyCost(cost)) {
             setDead();
         }
     }
 
+    public float getRadius(float tickDelta) {
+        return MathHelper.lerp(tickDelta, prevRadius, radius);
+    }
+
     /**
      * Calculates the maximum radius of the shield. aka The area of effect.
      */
     public double getDrawDropOffRange(Caster<?> source) {
-        float multiplier = source instanceof Pony pony && pony.asEntity().isSneaking() ? 1 : 2;
+        targetRangeMultiplier = source instanceof Pony pony && pony.asEntity().isSneaking() ? 1 : 2;
+        if (rangeMultiplier < targetRangeMultiplier - 0.1F) {
+            rangeMultiplier += 0.1F;
+        } else if (rangeMultiplier > targetRangeMultiplier + 0.1) {
+            rangeMultiplier -= 0.1F;
+        } else {
+            rangeMultiplier = targetRangeMultiplier;
+        }
+
         float min = (source instanceof Pony ? 4 : 6) + getTraits().get(Trait.POWER);
-        double range = (min + (source.getLevel().getScaled(source instanceof Pony ? 4 : 40) * (source instanceof Pony ? 2 : 10))) / multiplier;
+        double range = (min + (source.getLevel().getScaled(source instanceof Pony ? 4 : 40) * (source instanceof Pony ? 2 : 10))) / rangeMultiplier;
 
         return range;
     }
@@ -150,7 +161,7 @@ public class ShieldSpell extends AbstractSpell {
     }
 
     protected long applyEntities(Caster<?> source) {
-        double radius = getDrawDropOffRange(source);
+        double radius = this.radius;
 
         Vec3d origin = getOrigin(source);
 
diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/MagicParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/MagicParticle.java
index 700963a6..fd40922f 100644
--- a/src/main/java/com/minelittlepony/unicopia/client/particle/MagicParticle.java
+++ b/src/main/java/com/minelittlepony/unicopia/client/particle/MagicParticle.java
@@ -22,9 +22,9 @@ public class MagicParticle extends SpriteBillboardParticle {
         velocityX = vX;
         velocityY = vY;
         velocityZ = vZ;
-        startX = x + random.nextGaussian()/3;
-        startY = y + random.nextGaussian()/3;
-        startZ = z + random.nextGaussian()/3;
+        startX = x;// + random.nextGaussian()/3;
+        startY = y;// + random.nextGaussian()/3;
+        startZ = z;// + random.nextGaussian()/3;
         scale = random.nextFloat() * 0.12F;
         maxAge = (int)(Math.random() * 10) + 20;
 
diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/RenderLayers.java b/src/main/java/com/minelittlepony/unicopia/client/render/RenderLayers.java
index 80eeefd4..5f58b7b1 100644
--- a/src/main/java/com/minelittlepony/unicopia/client/render/RenderLayers.java
+++ b/src/main/java/com/minelittlepony/unicopia/client/render/RenderLayers.java
@@ -72,7 +72,15 @@ public final class RenderLayers extends RenderLayer {
     }
 
     public static RenderLayer getMagicNoColor() {
-        return MAGIC_NO_COLOR;
+        //return MAGIC_NO_COLOR;
+        return of("magic_no_color", VertexFormats.POSITION_COLOR_TEXTURE_OVERLAY_LIGHT_NORMAL,
+                VertexFormat.DrawMode.QUADS, 256, true, true, MultiPhaseParameters.builder()
+                .program(COLOR_PROGRAM)
+                .transparency(TRANSLUCENT_TRANSPARENCY)
+                .target(TRANSLUCENT_TARGET)
+                .cull(DISABLE_CULLING)
+                .writeMaskState(COLOR_MASK)
+            .build(false));
     }
 
     public static RenderLayer getMagicColored() {
diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java
index f868ff04..694571c3 100644
--- a/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java
+++ b/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java
@@ -3,38 +3,37 @@ package com.minelittlepony.unicopia.client.render.model;
 import java.util.ArrayList;
 import java.util.List;
 
-import org.joml.Matrix4f;
+import org.joml.Vector3f;
 import org.joml.Vector4f;
 
+import com.minelittlepony.unicopia.client.render.RenderUtil;
+
 import net.minecraft.client.render.VertexConsumer;
 import net.minecraft.client.util.math.MatrixStack;
 
 public class BakedModel {
-    private static final Vector4f drawVert = new Vector4f();
-
-    protected final List<Vertex> vertices = new ArrayList<>();
+    protected final List<RenderUtil.Vertex> vertices = new ArrayList<>();
 
     protected void addVertex(Vector4f vertex) {
         addVertex(vertex.x, vertex.y, vertex.z, 0, 0);
     }
 
     protected void addVertex(float x, float y, float z, float u, float v) {
-        vertices.add(new Vertex(x, y, z, u, v));
+        vertices.add(new RenderUtil.Vertex(new Vector3f(x, y, z), u, v));
     }
 
-    public final void render(MatrixStack matrices, VertexConsumer vertexWriter, int light, int overlay, float scale, float r, float g, float b, float a) {
+    public final void render(MatrixStack matrices, VertexConsumer buffer, int light, int overlay, float scale, float r, float g, float b, float a) {
         scale = Math.abs(scale);
         if (scale < 0.001F) {
             return;
         }
 
-        Matrix4f model = matrices.peek().getPositionMatrix();
-        for (Vertex vertex : vertices) {
-            drawVert.set(vertex.x() * scale, vertex.y() * scale, vertex.z() * scale, 1);
-            drawVert.mul(model);
-            vertexWriter.vertex(drawVert.x, drawVert.y, drawVert.z, r, g, b, a, vertex.u(), vertex.v(), overlay, light, 0, 0, 0);
+        matrices.push();
+        matrices.scale(scale, scale, scale);
+        for (RenderUtil.Vertex vertex : vertices) {
+            Vector4f pos = vertex.position(matrices);
+            buffer.vertex(pos.x, pos.y, pos.z, r, g, b, a, vertex.u(), vertex.v(), overlay, light, 0, 0, 0);
         }
+        matrices.pop();
     }
-
-    record Vertex(float x, float y, float z, float u, float v) {}
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java
new file mode 100644
index 00000000..45731fd0
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java
@@ -0,0 +1,41 @@
+package com.minelittlepony.unicopia.client.render.spell;
+
+import com.minelittlepony.common.util.Color;
+import com.minelittlepony.unicopia.ability.magic.Caster;
+import com.minelittlepony.unicopia.ability.magic.spell.effect.ShieldSpell;
+import com.minelittlepony.unicopia.client.render.RenderLayers;
+import com.minelittlepony.unicopia.client.render.model.SphereModel;
+import com.minelittlepony.unicopia.util.ColorHelper;
+
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.render.VertexConsumer;
+import net.minecraft.client.render.VertexConsumerProvider;
+import net.minecraft.client.render.VertexConsumerProvider.Immediate;
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.util.math.MathHelper;
+
+public class ShieldSpellRenderer implements SpellRenderer<ShieldSpell> {
+    @Override
+    public void render(MatrixStack matrices, VertexConsumerProvider vertices, ShieldSpell spell, Caster<?> caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) {
+        matrices.push();
+        double height = caster.asEntity().getEyeY() - caster.getOriginVector().y;
+        matrices.translate(0, height, 0);
+
+        Immediate immediate = MinecraftClient.getInstance().getBufferBuilders().getEntityVertexConsumers();
+        immediate.draw();
+
+        int color = ColorHelper.lerp(caster.getCorruption().getScaled(1) * (tickDelta / (1 + caster.asWorld().random.nextFloat())), spell.getType().getColor(), 0xFF000);
+        float[] colors = ColorHelper.changeSaturation(Color.r(color), Color.g(color), Color.b(color), 4);
+        float radius = 0.35F + spell.getRadius(tickDelta) + MathHelper.sin(animationProgress / 30F) * 0.01F;
+
+        VertexConsumer buffer = vertices.getBuffer(RenderLayers.getMagicNoColor());
+
+        float thickness = 0.02F * MathHelper.sin(animationProgress / 30F);
+        float alpha = 1 - Math.abs(MathHelper.sin(animationProgress / 20F)) * 0.2F;
+        SphereModel.SPHERE.render(matrices, buffer, light, 1, radius + thickness, colors[0], colors[1], colors[2], alpha * 0.08F);
+        SphereModel.SPHERE.render(matrices, buffer, light, 1, radius - thickness, colors[0], colors[1], colors[2], alpha * 0.05F);
+        SphereModel.SPHERE.render(matrices, buffer, light, 1, radius + thickness * 2, colors[0], colors[1], colors[2], alpha * 0.05F);
+
+        matrices.pop();
+    }
+}
diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java
index f44aad5f..692a8e89 100644
--- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java
+++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java
@@ -45,6 +45,7 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader
 
     static {
         register(SpellType.PLACED_SPELL, PlacedSpellRenderer::new);
+        register(SpellType.SHIELD, ShieldSpellRenderer::new);
     }
 
     @Nullable