# Particle tracking in ImageJ (TrackMate) and MSD: a practical guide

> Track particles in ImageJ with TrackMate (Fiji) or Find Maxima, then get the diffusion coefficient from the MSD: MSD = 4Dt in 2D, with a worked example.

Published 2026-11-06 by LabCanvas team. https://www.labcanvas.com/blog/particle-tracking-msd

![ImageJ Z Project dialog set to Max Intensity over frames 1 to 50 of a synthetic particle time-lapse, and the maximum projection showing the path of every particle as a trail](https://www.labcanvas.com/blog/imagej/particle-tracking-msd-z-project.webp)

To track particles in ImageJ, use **TrackMate** in Fiji (**Plugins › Tracking › TrackMate**): it finds the spots in every frame, links them into tracks and exports them. Then compute the mean squared displacement (MSD) of the tracks for each lag time and fit a line: in two dimensions \(\mathrm{MSD} = 4Dt\), so the diffusion coefficient **\(D\) is the slope divided by 4**. Plain ImageJ 1.x has no tracker, but **Process › Find Maxima** per frame and a short macro (below) do the same job for well-separated spots.

$$\mathrm{MSD}(\tau) = \big\langle \lvert \mathbf{r}(t + \tau) - \mathbf{r}(t) \rvert^2 \big\rangle$$

and for free diffusion

$$\mathrm{MSD} = 2 \cdot d \cdot D \cdot \tau \qquad (4D\tau \text{ in 2D},\ 6D\tau \text{ in 3D})$$

The worked example is a synthetic time-lapse with a known answer: 40 particles diffusing with D = 0.04 µm²/s on a drifting stage. Plain ImageJ with the macro gives D = 0.0405 µm²/s, within about 1% of the true value.

## What MSD and D mean

The **mean squared displacement** is how far, on average, a particle has moved after a lag time \(\tau\), squared so that moves to the left and right do not cancel. For each track you take every pair of positions \(\tau\) apart, square the distance between them, and average over all pairs and all tracks.

For a particle in free Brownian motion, the MSD grows in a straight line with the lag. The **diffusion coefficient** \(D\) sets the slope: \(\mathrm{MSD} = 4D\tau\) in 2D. A small protein in cytoplasm has a D of a few µm²/s; a membrane receptor or a vesicle often sits between 0.001 and 0.1 µm²/s.

The shape of the curve tells you the kind of motion (Saxton and Jacobson 1997):

| MSD against lag | Motion | Model |
|---|---|---|
| A straight line | Free diffusion | \(\mathrm{MSD} = 4D\tau\) |
| Curves upwards | Directed motion, flow or uncorrected drift | \(\mathrm{MSD} = 4D\tau + v^2\tau^2\) |
| Flattens to a plateau | Confined in a region | Plateau ≈ size of the region squared |
| Below the line on a log-log plot (slope \(\alpha < 1\)) | Anomalous (sub-) diffusion | \(\mathrm{MSD} = 4D\tau^\alpha\) |

## Particle tracking in Fiji: TrackMate step by step

TrackMate (Tinevez et al. 2017; version 7, Ershov et al. 2022) ships with Fiji. It works as a wizard: one panel per step, with **Next** and **Previous** at the bottom.

1. **Check the calibration.** Open the time-lapse and look at **Image › Properties…** (Ctrl Shift P): the pixel size in µm, and the stack as frames (t), not slices (z). ImageJ treats a stack as a Z-stack when the file does not say otherwise; swap the two numbers if needed. Then start **Plugins › Tracking › TrackMate**; its first panel repeats the calibration and lets you limit the analysis to a region.
2. **Choose a detector.** The **LoG detector** (Laplacian of Gaussian) suits spots of about 5 to 20 pixels across; the **DoG detector** is faster and suits small spots under about 5 pixels. TrackMate 7 adds detectors for larger objects, including thresholding, label images and plugins for StarDist and Cellpose.
3. **Set the detector.** Type the estimated diameter of your spots (**Estimated object diameter**; **blob diameter** in older versions) in physical units (µm), tick **Sub-pixel localization**, and use **Preview** while you raise the **Quality threshold** until each particle gets one circle and the noise gets none.
4. **Filter the spots** on any feature (quality, mean intensity, contrast) with the histograms on the filter panel.
5. **Choose a tracker.** The **Simple LAP tracker** links spots by distance and closes gaps; the full **LAP tracker** also allows splitting and merging and feature penalties. Both follow the linear assignment approach of Jaqaman et al. (2008).
6. **Set the tracker.** **Linking max distance** is the largest move between two frames (a little more than the fastest real step, less than the distance between neighbours). **Gap-closing max distance** and **Gap-closing max frame gap** let a particle that blinks or leaves focus for a frame or two stay one track.
7. **Filter the tracks**, for example on the number of spots, to drop tracks too short for an MSD.
8. **Export.** On the display panel, the **Tracks** button opens the results tables: **Spots** (one row per detection, with its track ID, frame and X, Y), **Edges** (the links) and **Tracks**, each of which can be exported as CSV. **Save** writes the whole session as an XML file you can load again.

TrackMate stops there for MSD. It reports per-track speed, displacement and duration, but its standard panels do not plot the MSD or fit D. Take the spots table (track ID, frame, X, Y) into Python, R, MATLAB (the msdanalyzer class is a common choice) or a spreadsheet, and follow the MSD steps below.

## Particle tracking in plain ImageJ: Find Maxima, a projection, and a short macro

Plain ImageJ 1.x has no tracker, but it has everything needed for clear, well-separated spots. This is the route used for the worked example.

### Step 1: find the spots in one frame

Select the first frame and run **Process › Find Maxima…**. Set **Prominence >** (how far a spot must stand out from its surroundings) so that each particle gets one point and the background none; tick **Preview point selection** while you adjust it. **Output type: List** puts the X and Y of every spot in a Results table.

![ImageJ Find Maxima dialog over the first frame of the particle time-lapse, with the Results table of X and Y positions](https://www.labcanvas.com/blog/imagej/particle-tracking-msd-find-maxima.webp "ImageJ 1.54f on frame 1 of the synthetic time-lapse: Find Maxima with a prominence of 300 1 and edge spots skipped 2, output as a List 3. The Results table has one row per particle, 40 in all 4. The yellow marks are the same maxima shown as points.")

Find Maxima gives whole-pixel positions. That is fine when the steps are several pixels long, as here (about 3 pixels per frame); for slow particles TrackMate's sub-pixel fit is worth having.

### Step 2: see the paths with a Z projection

**Image › Stacks › Z Project…** with **Max Intensity** over all frames puts every frame on top of the others, so each particle's path appears as a trail. It is the quickest check that particles move in a random walk and do not cross each other's paths, which is what makes simple linking safe.

![ImageJ Z Project dialog with Max Intensity, and the maximum projection showing every particle path as a trail](https://www.labcanvas.com/blog/imagej/particle-tracking-msd-z-project.webp "ImageJ 1.54f: Z Project of frames 1 to 50 1 with Max Intensity 2. Each trail is one particle wandering for 49 s 3; the trails stay apart, so nearest-neighbour linking will not mix them up.")

![Four panels: frame 1, frame 50, the maximum projection and the 40 linked tracks in colour](https://www.labcanvas.com/blog/imagej/particle-tracking-msd-frames-grid.webp "The synthetic time-lapse in ImageJ 1.54f: the first and last frames, the maximum projection of all 50, and the 40 tracks the macro below linked from the Find Maxima positions, drawn as coloured lines over the projection.")

### Step 3: link the spots and compute the MSD

The macro below runs Find Maxima on every frame, links each spot to the nearest spot in the next frame (no farther than **maxStep** pixels, with up to **maxGap** missed frames), subtracts the drift of the whole field, computes the MSD for lags 1 to 10 and fits the first four. Open the time-lapse, set its pixel size and frame interval in **Image › Properties…**, then paste the macro into **Plugins › New › Macro** and click **Run**.

```
// Find spots in every frame (Find Maxima), link them to the nearest spot
// in the next frame, then compute the MSD and D (2D: MSD = 4Dt).
prom = 300;    // Find Maxima prominence
maxStep = 15;  // largest move between frames, pixels
maxGap = 3;    // frames a spot may be missing
maxLag = 10;   // lags for the MSD curve
nFit = 4;      // lags used for the fit
getPixelSize(unit, pw, ph);
dt = Stack.getFrameInterval(); if (dt == 0) dt = 1;
n = nSlices; maxT = 1000;
X = newArray(maxT*n); Y = newArray(maxT*n);
Array.fill(X, NaN); Array.fill(Y, NaN);
last = newArray(maxT); nt = 0;
for (f = 0; f < n; f++) {
  setSlice(f + 1);
  run("Find Maxima...", "prominence="+prom+" strict exclude output=List");
  xs = Table.getColumn("X", "Results"); ys = Table.getColumn("Y", "Results");
  used = newArray(xs.length);
  for (k = 0; k < nt; k++) {
    if (f - last[k] > maxGap) continue;
    lx = X[k*n + last[k]]; ly = Y[k*n + last[k]];
    best = -1; bd = maxStep;
    for (i = 0; i < xs.length; i++) {
      if (used[i]) continue;
      d = sqrt(pow(xs[i]-lx, 2) + pow(ys[i]-ly, 2));
      if (d < bd) { bd = d; best = i; }
    }
    if (best >= 0) { used[best] = 1; X[k*n+f] = xs[best]; Y[k*n+f] = ys[best]; last[k] = f; }
  }
  for (i = 0; i < xs.length; i++) if (!used[i] && nt < maxT) {
    X[nt*n+f] = xs[i]; Y[nt*n+f] = ys[i]; last[nt] = f; nt++;
  }
}
// Drift: the mean step per frame over all tracks
sx = 0; sy = 0; ns = 0;
for (k = 0; k < nt; k++) for (f = 0; f < n-1; f++)
  if (!isNaN(X[k*n+f]) && !isNaN(X[k*n+f+1])) { sx += X[k*n+f+1]-X[k*n+f]; sy += Y[k*n+f+1]-Y[k*n+f]; ns++; }
vx = sx/ns; vy = sy/ns;
// MSD per lag, drift subtracted, in calibrated units
lag = newArray(maxLag); msd = newArray(maxLag);
for (L = 1; L <= maxLag; L++) {
  s = 0; c = 0;
  for (k = 0; k < nt; k++) for (f = 0; f + L < n; f++)
    if (!isNaN(X[k*n+f]) && !isNaN(X[k*n+f+L])) {
      dx = (X[k*n+f+L]-X[k*n+f] - vx*L)*pw; dy = (Y[k*n+f+L]-Y[k*n+f] - vy*L)*ph;
      s += dx*dx + dy*dy; c++;
    }
  lag[L-1] = L*dt; msd[L-1] = s/c;
}
Table.create("MSD");
Table.setColumn("Lag (s)", lag); Table.setColumn("MSD ("+unit+"^2)", msd);
tf = Array.trim(lag, nFit); mf = Array.trim(msd, nFit);
Fit.doFit("Straight Line", tf, mf);
D = Fit.p(1)/4;
print(nt+" tracks; drift "+d2s(vx*pw/dt,4)+", "+d2s(vy*ph/dt,4)+" "+unit+"/s");
print("MSD slope "+d2s(Fit.p(1),4)+" "+unit+"^2/s, intercept "+d2s(Fit.p(0),4)+", R^2 "+d2s(Fit.rSquared,4));
print("D = slope/4 = "+d2s(D,4)+" "+unit+"^2/s");
Plot.create("MSD", "Lag time (s)", "MSD ("+unit+"^2)");
Plot.add("circle", lag, msd);
Plot.setColor("red");
Plot.drawLine(0, Fit.p(0), lag[nFit-1], Fit.p(0)+Fit.p(1)*lag[nFit-1]);
Plot.show();
```

Nearest-neighbour linking is the simplest tracker there is. It is safe when particles stay farther apart than a few steps, as the projection showed; for crowded fields, particles that cross, divide or merge, use TrackMate's LAP tracker.

## Worked example: D from a synthetic time-lapse

The sample is synthetic, made with known values: 40 particles in Brownian motion with **D = 0.04 µm²/s**, a stage drift of (0.05, 0.02) µm/s, 50 frames 1 s apart, 512 × 512 pixels of 0.1 µm. Three particles blink out for one or two frames, to test gap closing.

**One displacement by hand.** Track 1 starts at pixel (167, 348) and is at (171, 350) one frame later: a step of (4, 2) pixels, or (0.4, 0.2) µm. Its squared displacement is \(0.4^2 + 0.2^2 = 0.20\ \mu\mathrm{m}^2\). Averaging all 1,953 such one-frame steps over the 40 tracks, after taking off the drift, gives the MSD at a lag of 1 s: 0.165 µm².

**The whole curve.** ImageJ found 40 particles in every frame (1,996 positions; the four missing ones are the blinks), and the macro linked them into 40 tracks. The MSD rises by about 0.16 µm² per second of lag:

| Lag (s) | 1 | 2 | 3 | 4 | 6 | 8 | 10 |
|---|---|---|---|---|---|---|---|
| MSD (µm²) | 0.165 | 0.328 | 0.486 | 0.652 | 0.952 | 1.251 | 1.538 |

![ImageJ MSD table and plot: MSD against lag time with a straight-line fit through the first four lags](https://www.labcanvas.com/blog/imagej/particle-tracking-msd-plot.webp "ImageJ 1.54f, output of the macro on the synthetic time-lapse: the MSD for lags 1 to 10 s, and the straight line fitted to lags 1 to 4 1. Slope 0.1618 µm²/s 2, so D = 0.1618 ÷ 4 = 0.0405 µm²/s against a true value of 0.04.")

The straight line through lags 1 to 4 has a slope of **0.1618 µm²/s** (R² 0.9999), so \(D = 0.1618 \div 4 = 0.0405\ \mu\mathrm{m}^2/\mathrm{s}\), within about 1% of the true 0.04. The intercept is 0.0033 µm², which is the localisation error at work: whole-pixel positions add a rounding error of 1/12 pixel² per axis, and \(4 \times \tfrac{1}{12} \times 0.1^2\ \mu\mathrm{m}^2 = 0.0033\ \mu\mathrm{m}^2\).

**Drift matters.** The macro measured a drift of (0.046, 0.011) µm/s from the mean step of all particles. With the drift line switched off, the same fit gives a slope of 0.1735 µm²/s and D = 0.0434 µm²/s, 8% too high, and the curve bends upwards at long lags.

## Pitfalls

- **Speed means little for diffusing particles.** The "speed" of a Brownian particle (path length ÷ time) depends on the frame interval, not only on the particle: the step grows with the square root of the interval. On the sample, a particle with D = 0.04 µm²/s filmed once a second makes a mean step of about 0.35 µm, so its "speed" is about 0.35 µm/s; film it twice as fast and the "speed" rises by \(\sqrt{2}\). Report D for diffusion and speed for directed motion.
- **Wrong calibration, wrong D by a square.** D scales with the pixel size squared and with the frame interval. Check **Image › Properties…** before tracking.
- **Linking errors.** If the largest move is close to the distance between neighbours, tracks swap partners and the MSD is too high. Keep particles more than a few steps apart (lower density, faster frames), and check the tracks on the image.
- **Fitting too many lags.** The long lags rest on few displacements and are noisy. Fit the first few (four is common; Michalet 2010 gives the best number for your localisation error).
- **Forcing the line through zero.** Localisation error adds a constant \(4\sigma^2\) to the MSD (\(\sigma\) is the position error per axis). Keep the intercept in the fit, or D comes out wrong when steps are small.
- **Short tracks.** An MSD from 5-frame tracks is mostly noise. Filter out short tracks, and report how many tracks and steps went into D.
- **Drift and flow.** An MSD that curves up is drift or transport. Subtract the stage drift (from fixed beads, or the mean motion of all particles when they move independently), or fit \(\mathrm{MSD} = 4D\tau + v^2\tau^2\) if the transport is real.

## Reporting it

A methods sentence to copy, with the parts in brackets filled in:

> Particles were tracked in [Fiji (TrackMate 7, DoG detector, estimated diameter 0.5 µm, sub-pixel localisation; Simple LAP tracker, linking distance 1 µm, gap closing 2 frames)] on [time-lapse, 50 frames at 1 s, 0.1 µm per pixel]. Tracks shorter than [10] frames were excluded. Stage drift was subtracted as the mean displacement of all particles per frame. The mean squared displacement was computed over all tracks and fitted with MSD = 4Dτ + c over the first [4] lags; D is given as mean ± SD over [n] cells from [3] independent experiments.

Cite TrackMate (Tinevez et al. 2017, or Ershov et al. 2022 for version 7) and Fiji (Schindelin et al. 2012). To show the result, plot the MSD against lag with the fit, and D per cell as points with the mean ± SD across replicates; a few tracks drawn over the first frame, coloured by track, make the best figure panel.

## An easier way: tracking and D in LabCanvas

If you would rather not write a macro or move TrackMate tables into another program, [LabCanvas Image Analysis](https://www.labcanvas.com/analysis.html) tracks and fits D in the browser: **Assays › Track particles and cells**.

1. Open the time-lapse (an ImageJ or OME-TIFF hyperstack keeps its pixel size and frame interval), or click **Try it on a sample** for the same synthetic movie used above.
2. **Detect**: **Spots** (local maxima, prominence set from the noise) or **Objects** (a threshold per frame, for cells).
3. **Link**: **Largest move between frames**, **Frames that may be missed**, **Shortest track** and **Lags for the fit of D**; tick **Subtract the drift for the MSD**.
4. Read the summary (tracks, mean speed, \(D\) from \(\mathrm{MSD} = 4 \cdot D \cdot t + c\), drift) and the per-track table, and **Send to Graphs** to compare cells or conditions.

![Particle and cell tracking in LabCanvas Image Analysis](https://www.labcanvas.com/analysis-examples/tracking.webp "Particle and cell tracking: 40 tracks, speed 0.358 ± 0.022 µm/s, diffusion coefficient D 0.0392 µm²/s.")

On the sample it finds 40 tracks and **D = 0.0392 µm²/s**, against the true 0.04 and the 0.0405 from plain ImageJ with the macro. Its detection is sub-pixel, so the MSD carries less localisation error than whole-pixel Find Maxima positions.

Why it is a good choice for 2D tracking:

- **No macro and no plugin**: detection, linking with gap closing, drift subtraction and the MSD fit in one panel.
- **Nothing to install**: it runs in the browser on any computer or tablet.
- **Straight into Graphs**: per-track duration, path length, displacement, speed and straightness go to Graphs for statistics, with a methods sentence to copy.
- **Free with a LabCanvas account**; downloading CSV files is part of LabCanvas Pro. Every measurement is checked in [more than 4,000 automated tests](https://www.labcanvas.com/image-analysis-validation).

TrackMate remains the better tool for 3D tracking, for cells that divide and merge, for crowded fields that need a global LAP solution, and for StarDist or Cellpose segmentation before tracking; and LabCanvas needs .czi, .nd2 or .lif files exported as OME-TIFF first. ImageJ and Fiji are independent open-source projects; LabCanvas is not affiliated with them.

## References

- Ershov D, Phan MS, Pylvänäinen JW, Rigaud SU, Le Blanc L, Charles-Orszag A, et al. (2022). TrackMate 7: integrating state-of-the-art segmentation algorithms into tracking pipelines. *Nature Methods* 19, 829–832. [doi:10.1038/s41592-022-01507-1](https://doi.org/10.1038/s41592-022-01507-1)
- Jaqaman K, Loerke D, Mettlen M, Kuwata H, Grinstein S, Schmid SL, Danuser G (2008). Robust single-particle tracking in live-cell time-lapse sequences. *Nature Methods* 5, 695–702. [doi:10.1038/nmeth.1237](https://doi.org/10.1038/nmeth.1237)
- Michalet X (2010). Mean square displacement analysis of single-particle trajectories with localization error: Brownian motion in an isotropic medium. *Physical Review E* 82, 041914. [doi:10.1103/PhysRevE.82.041914](https://doi.org/10.1103/PhysRevE.82.041914)
- Saxton MJ, Jacobson K (1997). Single-particle tracking: applications to membrane dynamics. *Annual Review of Biophysics and Biomolecular Structure* 26, 373–399. [doi:10.1146/annurev.biophys.26.1.373](https://doi.org/10.1146/annurev.biophys.26.1.373)
- Schindelin J, Arganda-Carreras I, Frise E, et al. (2012). Fiji: an open-source platform for biological-image analysis. *Nature Methods* 9, 676–682. [doi:10.1038/nmeth.2019](https://doi.org/10.1038/nmeth.2019)
- Tinevez JY, Perry N, Schindelin J, Hoopes GM, Reynolds GD, Laplantine E, Bednarek SY, Shorte SL, Eliceiri KW (2017). TrackMate: an open and extensible platform for single-particle tracking. *Methods* 115, 80–90. [doi:10.1016/j.ymeth.2016.09.016](https://doi.org/10.1016/j.ymeth.2016.09.016)

## Frequently asked questions

### Is there a particle tracker in ImageJ?

Plain ImageJ 1.x has no tracking command. Fiji includes TrackMate (Plugins › Tracking › TrackMate), which detects spots in every frame, links them into tracks with a LAP tracker, closes gaps and exports the tracks. In plain ImageJ you can detect spots per frame with Process › Find Maxima and link them with a short macro, as shown in this guide.

### How do I calculate the diffusion coefficient from MSD?

Plot the mean squared displacement against the lag time and fit a straight line to the first few lags. For free diffusion in two dimensions \(\mathrm{MSD} = 4Dt\), so \(D\) is the slope divided by 4 (by 2 in 1D, by 6 in 3D). Keep the intercept in the fit: it absorbs the localisation error.

### Does TrackMate calculate MSD?

TrackMate gives per-track features such as speed, displacement and duration, and exports spots, links and tracks to CSV or XML, but its standard panels do not fit an MSD curve. Most people export the tracks and compute the MSD in Python, R, MATLAB (msdanalyzer) or a spreadsheet, or use a tool that fits it directly.

### How many lags should I use to fit the MSD?

The first few: the short lags are averaged over many more displacements, so they are the most precise. Four lags is a common choice for free diffusion; Michalet (2010) works out the best number from the localisation error and the track length. Never fit out to the full track length, where each point rests on a handful of displacements.

### Why is my MSD curve bending upwards?

An MSD that curves upwards means directed motion or drift: \(\mathrm{MSD} = 4Dt + v^2 t^2\). Subtract the stage drift (the mean motion of all particles) before computing the MSD, or fit \(4Dt + v^2 t^2\) if the particles really are transported. A curve that flattens means confined motion.

### What units does D have?

Length squared per time, usually µm²/s. Set the pixel size and the frame interval first (Image › Properties in ImageJ), or the result comes out in pixels² per frame.

## Related links

- [Maximum intensity projection](https://www.labcanvas.com/blog/maximum-intensity-projection): The six Z Project methods with pictures
- [FRAP analysis: mobile fraction and half-time](https://www.labcanvas.com/blog/frap-analysis-mobile-fraction): Diffusion from recovery instead of tracks
- [Fiji vs ImageJ](https://www.labcanvas.com/blog/fiji-vs-imagej): Which one has TrackMate, and why
- [Image Analysis: the complete guide](https://www.labcanvas.com/blog/image-analysis-complete-guide): Every tool and assay in the app
