Does anyone know how to create a ghost trail effect? For a simple example look at this screenshot:

Ghosting Effect

You can find the actual animation here. What I would ultimately like to see it happen is to make the object move based on whatever equations you specify it. For instance, to make it move around a circle the object should have the position (cos[t], sin[t]). Or, lets say you have a list of specified coordinates {(x1,y1), (x2,y2), ..., (xn,yn)}, All I want to be able to see is the trace as an object takes in the coordinates I specify.

Here is a simple ball moving without the ghosting effect.

  Disk[{Cos[u], Sin[u]}, .25],
  PlotRange -> {{-2, 2}, {-2, 2}},
  ImageSize -> 400, 
  Axes -> True
 {u, 0, 6}

enter image description here

  • 1
    $\begingroup$ As you asked for, I've added the gif animation code to R.M's answer - the "DisplayDuration" setting is measured in seconds, and as far as I know one shouldn't go below 0.03 to avoid display problems in certain browsers. $\endgroup$
    – Jens
    Commented Apr 28, 2012 at 3:07

5 Answers 5


Here is a simple approach to create a ghost trail:

obj[{xfunc_, yfunc_}, rad_, lag_, npts_][x_] := MapThread[
 {Opacity[#1, ColorData["SunsetColors", #1]],
  Disk[{xfunc@#2, yfunc@#2}, rad Exp[#1 - 1]]} &, 
 Through[{Rescale, Identity}[Range[x - lag, x, lag/npts]]]]

frames = Most@Table[Graphics[obj[{Sin[2 #] &, Sin[3 #] &}, 0.1, 1, 500][u], 
  PlotRange -> {{-2, 2}, {-2, 2}}, Axes -> False, ImageSize -> 300,
  Background -> Black] ~Blur~ 3, {u, 0, 2 Pi, 0.1}];

Export["trail.gif", frames, "DisplayDurations" -> .03] 

enter image description here

  • $\begingroup$ Can someone upload a faster version of the gif? I forgot to set the duration and I got to run now. $\endgroup$
    – rm -rf
    Commented Apr 28, 2012 at 0:24
  • 2
    $\begingroup$ I uploaded a faster gif, don't look too long or you'll get dizzy. $\endgroup$
    – Jens
    Commented Apr 28, 2012 at 1:09
  • 1
    $\begingroup$ @Jens, Can you show us the uneducated on making gifs how to do it? $\endgroup$
    – jmlopez
    Commented Apr 28, 2012 at 1:54
  • 3
    $\begingroup$ And if you do mind: stare at the animation and repeat after me: "My limbs are getting heavy, I feel sleepy, I don't mind, I don't mind anything,..." $\endgroup$
    – Jens
    Commented Apr 28, 2012 at 2:53
  • 1
    $\begingroup$ @jmlopez You could do that using Interpolationreference.wolfram.com/mathematica/ref/Interpolation.html $\endgroup$
    – Ajasja
    Commented Apr 28, 2012 at 6:24

The idea behind this solution is to construct a superposition of Gaussian surfaces whose amplitude decay in time, and use DensityPlot to plot the trail:

trail[fun_, {t_, tmin_, tmax_, dt_}, k_, lam_][xxx_, yyy_] :=
  Module[{trange, xrange, yrange, twindow, trailf, sel, decayf},
   decayf[x0_, y0_, t0_] := Exp[-k t0 - lam^2 (x0^2 + y0^2)];
   twindow = 6/k;
   trange = Range[Max[tmin, tmax - twindow], tmax, dt];
   {xrange, yrange} = Transpose[fun /. t -> # & /@ trange];
   Fold[Function[{ff, pt}, 
        ff[#1, #2, #3] + (1 - ff @@ pt) decayf[pt[[1]] - #1, 
           pt[[2]] - #2, tmax - pt[[3]]]] &], 0 &, 
     Transpose[{xrange, yrange, trange}]][xxx, yyy]

In trail, fun is the path of the particle, tmin is the starting time, tmax is the current time, dt is the step size in time, and {xxx, yyy} are the coordinates at which you want to evaluate the function. The parameters k and lam determine the length and width of the trail where greater values for the parameters correspond to shorter/narrower trails.


Monitor[tab = 
      trail[{Cos[t], Sin[t]} + .5 {Cos[2 t], Sin[2 t]}, {t, 0, tt, 
         Pi/40}, 2, 5][x, y]],
     {x, -2, 2}, {y, -2, 2}, Frame -> False, Axes -> False, 
     PlotRange -> {0, 1}, PlotPoints -> 100, 
     ColorFunctionScaling -> False, ColorFunction -> "SunsetColors"],
    {tt, Pi + Range[40]/40 2 Pi}], tt];

Export["~/Desktop/trail3.gif", tab]


  • $\begingroup$ Pretty, as expected. :-) $\endgroup$
    – Mr.Wizard
    Commented Apr 28, 2012 at 14:51
  • $\begingroup$ Very nice indeed! +1 and if I could I would give some more. $\endgroup$
    – Lou
    Commented Apr 28, 2012 at 15:09
  • $\begingroup$ I love the graphics, but, is it possible to show the axes also, maybe change the color scheme? I'm having trouble doing the pretty rendering with a set of fixed points using the function Interpolation, any ideas of how to make it faster? $\endgroup$
    – jmlopez
    Commented Apr 28, 2012 at 18:06

With a bit of blur, but still not the variable-width blur in the example.

obj[{xfunc_, yfunc_}, rad_, lag_, npts_][x_] := 
 With[{trail = 
    Range[x - lag, x, lag/npts]}, {ColorData["SunsetColors"]@#1, 
     Opacity@#1, Disk[{xfunc@#2, yfunc@#2}, rad]} & @@@ 
   Transpose[{Rescale[trail], trail}]]

frames = Most@
   Table[ImageCompose[# ~Blur~ 4, 
       MapAt[Last, Show[#, Background -> None], 1]] &@
     Graphics[obj[{Cos, Sin}, 0.05, 1, 100][u], 
      PlotRange -> {{-2, 2}, {-2, 2}}, Axes -> False, 
      ImageSize -> 400, Axes -> True, Background -> Black], {u, 0, 
     2 Pi, .1}];

Export["trail.gif", frames, "DisplayDurations" -> .03]

enter image description here


I saw this question rather late, and I can't quite hope to outdo the very fine answers already given, so please allow me to share my modest attempt at a three-dimensional implementation of the ghost trail:

trail[f_, {t_, head_, len_}, r_, opts___] := 
 Block[{gc = First[Cases[ParametricPlot3D[f, {t, head - len, head}, 
       ColorFunction -> (Directive[Opacity[#4], Glow[ColorData["SunsetColors", #4]]] &), 
       Evaluate[Apply[Sequence, FilterRules[{opts}, 
          Options[ParametricPlot3D]]]]], _GraphicsComplex, Infinity]]},
  Graphics3D[{CapForm["Round"], JoinForm["Round"], 
    MapAt[gc[[1, #]] &, First[Cases[gc[[2]], 
       Line[l_, rest___] :> Tube[l, Map[Scaled[r #] &, 
          Exp[Cases[VertexColors /. gc[[3]], Opacity[op_] :> op - 1, Infinity][[l]]]], 
         VertexColors -> (VertexColors /. gc[[3]])[[l]]], Infinity]], 
     1]}, Method -> {"TubePoints" -> 25}, 
   Evaluate[Apply[Sequence, FilterRules[{opts}, Options[Graphics3D]]]], 
   Axes -> None, Background -> Black, Boxed -> False]]

I only wish to note three cute things about this particular implementation (that is, apart from the additional degree of freedom (via ViewPoint) provided by having three dimensions):

  1. We can leverage the adaptive sampling capability of ParametricPlot3D[] so that the curve traced out by the ghost trail is sampled only where it matters. (If need be, trail[] can of course take the PlotPoints option.)

  2. Tube[] supports a varying radius across its body. None of the two-dimensional primitives have an equivalent capability.

  3. The Opacity[] directive in the ColorFunction option setting, in addition to its obvious purpose, allows the trapping of the (scaled!) parameter values used in the sampling of the curve. These values are then easily retrieved for the conversion into the list of radii needed by Tube[].

As with R.M, I shall be using a Lissajous curve (the three-dimensional equivalent in this case) to demonstrate trail[]:

lissajous3D[n_, d_, a_, b_, c_][t_] := {a Sin[2 Pi n t + d], b Sin[2 Pi t], c Cos[2 Pi t]};

trailFrames = Table[Blur[trail[lissajous3D[3/4, 0, 9, 8, 4][t], {t, h, 1/5}, 1/100, 
                        ImageSize -> Medium, PlotRange -> {{-10, 10}, {-9, 9}, {-5, 5}}]],
                    {h, 0, 4 - 2/25, 2/25}];

Export["trail3D.gif", trailFrames, "DisplayDurations" -> .03]

3D ghost trail


In version 9, there is a new command ReplaceImageValue, and that got me thinking if it could be used to superimpose images more quickly. As an application, this question seemed to be a good illustration, especially under the aspect of speed.

To demonstrate a comparatively fast ghost trail effect, I tried this, and got a fun responsive interaction:

DynamicModule[{step, stencil, im, q},
    pos = MousePosition["Graphics"] /. None -> {200, 200}
   q = q + step[pos - q];
   im = ReplaceImageValue[
     ImageMultiply[im, .9],
     stencil /. Rule[arg1_, arg2_] :> Rule[q + arg1, arg2]
 Initialization :> (
   stencil = Map[Rule[
       # - {8, 8}, 
       Blend[{Yellow, RGBColor[0.3, 0., .3]}, 
        Norm[# - {10, 9}]/10.]] &,
     Position[DiskMatrix[7], 1]
   im = Image@Array[0 &, {300, 300}];
   q = {150, 150};
   step[v_] := 4. ArcTan[Norm[v]/3] Normalize[v]

ghost trail

Here the mouse controls where the ghost trail is heading. It follows the mouse in real-time.

To really exploit ReplaceImageValue here, I defined the bright spot in stencil as a disk with a gradient of colors using DiskMatrix[7] and assigning colors based on the distance from a point near its center. I deliberately offset that point: it's the subtracted value in Norm[# - {10, 9}]. If you replace {10, 9} by {8,8} you'll see that the offset gives the appearance of 3D lighting from one side.

To make the actual ghost trail, I used ImageMultiply on the currently displayed image im which darkens all pixels at every frame, so that the most recently added pixels from ReplaceImageValue appear brighter than the older pixels.

The position at which the stencil containing the disk is drawn is called q, and it gets updated by an increment in the function step which is directed toward the mouse but isn't allowed to jump too far ahead so that the trail doesn't get ripped into disjoint pieces (that's limited by the ArcTan).


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.