title: Fast WPF Particle Background
body: <p>I am building a WPF application and I want its background to be filled with <strong>particles</strong> with random:</p>
  
  <ul>
  <li>Opacity/z-order</li>
  <li>Size</li>
  <li>Velocity</li>
  <li>"<em>Fuzziness</em>" (blur effect)</li>
  <li>Directions (or path)</li>
  </ul>
  
  <p>I've found <a href="http://www.flashden.net/item/background-animation-blurry-light-cells/21980" rel="noreferrer">a really good example</a> of what I'd like it to be, but unfortunately it's in Flash and it's not free...</p>
  
  <p>I've tried to implement it but I can't manage to get it <strong>smooth</strong>...</p>
  
  <p>So I was wondering if any of you could help me improve it in order to get it to use less CPU and more GPU so it is smoother, even with more particles and in full screen mode.</p>
  
  <p><strong><em>Code "Particle.cs"</em></strong>: the class that defines a Particle with all its properties</p>
  
  <pre><code>public class Particle
  {
      public Point3D Position { get; set; }
      public Point3D Velocity { get; set; }
      public double Size { get; set; }
  
      public Ellipse Ellipse { get; set; }
  
      public BlurEffect Blur { get; set; }
      public Brush Brush { get; set; }
  }
  </code></pre>
  
  <p><strong><em>XAML "Window1.xaml"</em></strong>: the window's xaml code composed of a radial background and a canvas to host particles</p>
  
  <pre><code>&lt;Window x:Class="Particles.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Window1" Height="600" Width="800" Loaded="Window_Loaded"&gt;
      &lt;Grid&gt;
          &lt;Grid.Background&gt;
              &lt;RadialGradientBrush Center="0.54326,0.45465" RadiusX="0.602049" RadiusY="1.02049" GradientOrigin="0.4326,0.45465"&gt;
                  &lt;GradientStop Color="#57ffe6" Offset="0"/&gt;
                  &lt;GradientStop Color="#008ee7" Offset="0.718518495559692"/&gt;
                  &lt;GradientStop Color="#2c0072" Offset="1"/&gt;
              &lt;/RadialGradientBrush&gt;
          &lt;/Grid.Background&gt;
          &lt;Canvas x:Name="ParticleHost" /&gt;
      &lt;/Grid&gt;
  &lt;/Window&gt;
  </code></pre>
  
  <p><strong><em>Code "Window1.xaml.cs"</em></strong>: where everything happens</p>
  
  <pre><code>public partial class Window1 : Window
  {
      // ... some var/init code...
  
      private void Window_Loaded(object sender, RoutedEventArgs e)
      {
          timer.Interval = TimeSpan.FromMilliseconds(10);
          timer.Tick += new EventHandler(timer_Tick);
          timer.Start();
      }
  
      void timer_Tick(object sender, EventArgs e)
      {
          UpdateParticules();
      }
  
      double elapsed = 0.1;
      private void UpdateParticules()
      {
          // clear dead particles list
          deadList.Clear();
          // update existing particles
          foreach (Particle p in this.particles)
          {
              // kill a particle when its too high or on the sides
              if (p.Position.Y &lt; -p.Size || p.Position.X &lt; -p.Size || p.Position.X &gt; Width + p.Size)
              {
                  deadList.Add(p);
              }
              else
              {
                  // update position
                  p.Position.X += p.Velocity.X * elapsed;
                  p.Position.Y += p.Velocity.Y * elapsed;
                  p.Position.Z += p.Velocity.Z * elapsed;
                  TranslateTransform t = (p.Ellipse.RenderTransform as TranslateTransform);
                  t.X = p.Position.X;
                  t.Y = p.Position.Y;
  
                  // update brush/blur
                  p.Ellipse.Fill = p.Brush;
                  p.Ellipse.Effect = p.Blur;
              }
          }
  
          // create new particles (up to 10 or 25)
          for (int i = 0; i &lt; 10 &amp;&amp; this.particles.Count &lt; 25; i++)
          {
              // attempt to recycle ellipses if they are in the deadlist
              if (deadList.Count - 1 &gt;= i)
              {
                  SpawnParticle(deadList[i].Ellipse);
                  deadList[i].Ellipse = null;
              }
              else
              {
                  SpawnParticle(null);
              }
          }
  
          // Remove dead particles
          foreach (Particle p in deadList)
          {
              if (p.Ellipse != null) ParticleHost.Children.Remove(p.Ellipse);
              this.particles.Remove(p);
          }
      }
  
      private void SpawnParticle(Ellipse e)
      {
          // Randomization
          double x = RandomWithVariance(Width / 2, Width / 2);
          double y = Height;
          double z = 10 * (random.NextDouble() * 100);
          double speed = RandomWithVariance(20, 15);
          double size = RandomWithVariance(75, 50);
  
          Particle p = new Particle();
          p.Position = new Point3D(x, y, z);
          p.Size = size;
  
          // Blur
          var blur = new BlurEffect();
          blur.RenderingBias = RenderingBias.Performance;
          blur.Radius = RandomWithVariance(10, 15);
          p.Blur = blur;
  
          // Brush (for opacity)
          var brush = (Brush)Brushes.White.Clone();
          brush.Opacity = RandomWithVariance(0.5, 0.5);
          p.Brush = brush;
  
          TranslateTransform t;
  
          if (e != null) // re-use
          {
              e.Fill = null;
              e.Width = e.Height = size;
              p.Ellipse = e;
  
              t = e.RenderTransform as TranslateTransform;
          }
          else
          {
              p.Ellipse = new Ellipse();
              p.Ellipse.Width = p.Ellipse.Height = size;
              this.ParticleHost.Children.Add(p.Ellipse);
  
              t = new TranslateTransform();
              p.Ellipse.RenderTransform = t;
              p.Ellipse.RenderTransformOrigin = new Point(0.5, 0.5);
          }
  
          t.X = p.Position.X;
          t.Y = p.Position.Y;
  
          // Speed
          double velocityMultiplier = (random.NextDouble() + 0.25) * speed;
          double vX = (1.0 - (random.NextDouble() * 2.0)) * velocityMultiplier;
          // Only going from the bottom of the screen to the top (for now)
          double vY = -Math.Abs((1.0 - (random.NextDouble() * 2.0)) * velocityMultiplier);
  
          p.Velocity = new Point3D(vX, vY, 0);
          this.particles.Add(p);
      }
  
  
      private double RandomWithVariance(double midvalue, double variance)
      {
          double min = Math.Max(midvalue - (variance / 2), 0);
          double max = midvalue + (variance / 2);
          double value = min + ((max - min) * random.NextDouble());
          return value;
      }
  }
  </code></pre>
  
  <p>Thanks a lot!</p>