Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

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

share|improve this question
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. –  Jens Apr 28 '12 at 3:07

5 Answers 5

up vote 39 down vote accepted

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

share|improve this answer
Can someone upload a faster version of the gif? I forgot to set the duration and I got to run now. –  The Toad Apr 28 '12 at 0:24
I uploaded a faster gif, don't look too long or you'll get dizzy. –  Jens Apr 28 '12 at 1:09
@Jens, Can you show us the uneducated on making gifs how to do it? –  jmlopez Apr 28 '12 at 1:54
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,..." –  Jens Apr 28 '12 at 2:53
@jmlopez You could do that using Interpolationreference.wolfram.com/mathematica/ref/Interpolation.html –  Ajasja Apr 28 '12 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]


share|improve this answer
Pretty, as expected. :-) –  Mr.Wizard Apr 28 '12 at 14:51
Very nice indeed! +1 and if I could I would give some more. –  Lou Apr 28 '12 at 15:09
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? –  jmlopez Apr 28 '12 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

share|improve this answer

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

share|improve this answer
Other fun space curves to try include torus knots and clelies. –  Guess who it is. May 21 '12 at 17:39

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).

share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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