So i'll post my attempt here.
Idea is to have very simple particles (=speed(with friction)+position) that are layered.
The top (=0) layer shows the dust at scale 1, then every layer below has bigger scale, and those particles will move slower and slower the deeper the layer.
The difficulty here is to fine tune :
• appearance of the dust brushes (here random/with center gradient opacity).
• behavior of each particle, depending on its layer (i reduced the speed boost on lower layer, maybe the friction should be higher in lower layers too).
• size / number of particles per layer and such.
The first lines of the code are parameters that you can change to quite greatly change the way it looks.
http://jsfiddle.net/gamealchemist/4gfx1L2e/

// parameters
var layerCount = 3;
var dustSize = 50;
var dustPoints = 200;
var mouseRadius = 200;
var normalSpeed = 100;
var dustMaxOpacity = 0.85;
var baseScale = 1;
var friction = 0.1;
var partCountForLayer = function (layer, layerCount) {
var tmp = 1 + (layerCount - layer);
return 20 * tmp * tmp;
};
var dusts = []; // stores all dust image
var parts = []; // stores all particles
// create dust images
for (var i = 0; i < 5; i++) {
dusts.push(createDust(dustSize, dustPoints));
}
// Boilerplate
var cv = document.getElementById('cv');
var ctx = cv.getContext('2d');
// Part class : defines a dust particle
function Part(layer) {
this.x = cv.width * Math.random();
this.y = cv.height * Math.random();
this.vx = 0;
this.vy = 0;
this.layer = layer;
this.scale = baseScale * (1 + 3 * layer);
this.width = dustSize * this.scale;
this.heigth = dustSize * this.scale;
this.whichDust = Math.floor(dusts.length * Math.random());
this.draw = function () {
ctx.drawImage(dusts[this.whichDust], this.x, this.y, this.width, this.heigth);
};
// moves the dust with its current parameters
this.update = function () {
this.vx -= friction * this.vx;
this.vy -= friction * this.vy;
this.x += this.vx;
this.y += this.vy;
};
// move the dust with a brush in (x,y) having radius
// the normalized (0;1) move is stored in dx, dy
this.sweep = function (x, y, radius, dx, dy) {
var xDiff = this.x + this.width * 0.5 - x;
var yDiff = this.y + this.heigth * 0.5 - y;
var dist = Math.sqrt(xDiff * xDiff + yDiff * yDiff);
if ((dist > 0.5 * this.width) && (dist > radius)) return;
this.vx += normalSpeed * dx / (2 + 3 * this.layer);
this.vy += normalSpeed * dy / (2 + 3 * this.layer);
};
}
// fill the layers
for (var l = 0; l < layerCount; l++) {
var dustCount = partCountForLayer(l, layerCount);
for (var pi = 0; pi < dustCount; pi++) {
parts.push(new Part(l));
}
}
// -------------------------------------------
function animate() {
ctx.clearRect(0, 0, cv.width, cv.height);
ctx.fillText('-_ Swipe the dust with the mouse _-', 20, 20);
parts.forEach(function (p) {
p.draw();
p.update();
});
}
setInterval(animate, 30);
// moves all particles after a brush was used
function moveDust() {
console.log('md');
var dx = mouse.x - mouse.startX;
var dy = mouse.y - mouse.startY;
dx /= cv.width;
dy /= cv.height;
parts.forEach(function (p) {
p.sweep( mouse.startX, mouse.startY, mouseRadius, dx, dy)
});
}
// -------------------------------------------
// creates an image with random points drawn on it.
function createDust(size, count) {
var dustImage = document.createElement('canvas');
dustImage.width = size;
dustImage.height = size;
var dctx = dustImage.getContext('2d');
var sqRad = (0.5 * size) * (0.5 * size);
dctx.clearRect(0, 0, size, size);
for (var i = 0; i < count; i++) {
var x = (Math.random() - 0.5) * size;
var y = (Math.random() - 0.5) * size;
var d = x * x + y * y;
if (d > sqRad) continue;
dctx.globalAlpha = dustMaxOpacity * (1 - Math.sqrt(d / sqRad));
dctx.fillRect(x + 0.5 * size, y + 0.5 * size, 1, 1);
}
return dustImage;
}
// -------------------------------------------
// Mouse handling
// -------------------------------------------
function setupMouse(canvas, preventDefault) {
var rectLeft, rectTop;
var hook = canvas.addEventListener.bind(canvas);
hook('mousedown', updateMouseStatus.bind(null, true));
hook('mouseup', updateMouseStatus.bind(null, false));
hook('mouseout', updateMouseStatus.bind(null, null));
hook('mousemove', updateCoordinates);
window.addEventListener('scroll', updateRect);
function updateMouseStatus(b, e) {
mouse.down = !!b;
updateCoordinates(e);
if (b) {
mouse.startX = mouse.x;
mouse.startY = mouse.y;
} else if (b!=null) {
moveDust();
}
if (preventDefault) {
e.stopPropagation();
e.preventDefault();
}
}
function updateCoordinates(e) {
mouse.x = (e.clientX - rectLeft);
mouse.y = (e.clientY - rectTop);
}
function updateRect() {
var rect = canvas.getBoundingClientRect();
rectLeft = rect.left;
rectTop = rect.top;
}
updateRect();
};
var mouse = {
x: 0,
y: 0,
startX: 0,
startY: 0,
down: false
};
setupMouse(cv, true);