# Effective Fading Trail Effect in Mathematica

I'm trying to track the movement of a bunch of particles by adding fading trail effect and dynamically update particle's position. However, when I need to track several hundreds of them, drawing may take too much time, making dynamic updating impossible. So I would like to ask are there any way to create an effective fading trail effect in Mathematica, maybe proper tuning of Dynamic or drawing method may help.

The effect I would like to achieve is as follow:

(*Test data*)
priml = Table[# + {.07 k, Sin[.15 k]} & /@ Tuples[Range@15, 2], {k,
130}];
cf = Blend[{Yellow, Blend[{Orange, Red}]}, #/1.5] &;
coll = cf /@ Range[0., 1.5, 0.05];
sizel = .15 Range[0., 1.5, .05]^1.5;

(*Update position*)
Do[
FinishDynamic[];
track = priml[[i ;; i + 30]];
, {i, 100}] // AbsoluteTiming

(*Dynamically create the fading trail effect.*)
Dynamic@Graphics[
Function[{dat, size}, Disk[#, size] & /@ dat], {track, sizel}]}],
PlotRange -> {{-2, 25}, {-2, 17}}]


Well, but for simplicity, the first step could be setting sizel to a constant array ConstantArray[.15,31].

Actually the only change between two consecutive frames is the addition of new head, deletion of the tail, and the alternation in color, so I strongly suspect lot of time could be saved by proper updation instead of complete redraw the graphics.

In my real application, the calculation in a single Do loop takes about 0.5s, so a drawing time less than 0.05s would be satisfying.

Any idea?

Note: Slight decrease in quality is accepted, e.g. Raster method is allowed

• 4847 – Kuba Apr 7 '17 at 5:51
• sounds like a job for GPU/CUDA or at least for a Compile[expr,CompilationTarget->"C",Parallelization->True,RuntimeOptions->Listable] which will essentially add all of your trails in parallel over the entire list of your particles using machine-compiled C code. – Gregory Klopper Apr 7 '17 at 6:01
• Is the florid effect necessary? If you just need a fading trail, the implementation can be much easier and faster. – xzczd Apr 7 '17 at 6:50
• @xzczd yep,the color is necessary. But I suppose this effect could be easily achieved by ColorCombine. As the color function is just a linear blend so it won't be that hard. – Wjx Apr 7 '17 at 8:21
• For multiple copies of the same object it is often faster to use Translate. For example use MapThread[{#1, Translate[Disk[{0, 0}, #2], #3]} &, {coll, sizel, track}] as your Graphics expression. – Simon Woods Apr 7 '17 at 19:02

Errr, I found one partial solution with the help by @njpipeorgan, use raster image instead of vector image and use listable operations to update frames to ensure high efficiency.

(*Set plot range and length resolution*)
{{xmin, xmax}, {ymin, ymax}} = {{-2, 25}, {-2, 17}};
resolution = .05;

(*simple convertion*)
convert[{x_, y_}] := Round[{x - xmin, y - ymin}/resolution];
convert[s_] := Round[s/resolution]

(*Create a frame, a "frame" is given by a raster array here*)
createmat[cen_, r_] :=
Block[{arr = ConstantArray[0, convert[{xmax, ymax}]], pr = convert@r},
((arr[[#1 - pr ;; #1 + pr, #2 - pr ;; #2 + pr]] =
1.06 DiskMatrix@pr) & @@ convert@#) & /@ cen;
arr]

(*Update the current image, add new frame and make modification to older frames so that it looks like a fading trail: 1. color alternation 2. Shrinking in size*)
update[mat_, newprim_, r_] :=
Clip[mat +
createmat[newprim,
r] - .05 UnitStep[GradientFilter[mat, 1] - .2] - .06, {0, 1}]

(*Define frames and color used in rendering*)
priml = Table[# + {.07 k, Sin[.15 k]} & /@ Tuples[Range@15, 2], {k,
130}];
col1 = List @@ Yellow;
col2 = List @@ Blend[{Orange, Red}];

(*This function will convert a numeric 2-D array to a colored image using linear blend*)
restorecol[mat_, col1_, col2_] :=
ColorCombine[
Image[1 + Unitize[mat]*(mat #2 + (1 - mat) #1 - 1)] &, {col1,
col2}], "RGB"]

(*Dynamic!*)
Dynamic@restorecol[Reverse@Transpose@mat, col1, col2]

(*Initialization*)
mat = createmat[{}, .15];

(*Update*)
Do[
mat = update[mat, priml[[i]], .15],
{i, 100}]


The performance is satisfying on my computer, update a frame cost 0.06s in total.

But the usage of Raster is, in some ways, accelerating the process at a cost of decreased quality, so I still hope there could be a better answer!

Also, the performance still can be further improved, so any suggestions on this direction is also welcomed!