Yes, good point about Date.now(). In practice it won't make a big difference (unless you're doing it thousands of times per frame), but it's always good to use the faster option.
Regarding particle forces - I couldn't get JSDoc to generate any documentation for nested object properties. I've been playing with it for a while but failing. So here's the commented source code for particle forces - I hope this helps.
/** * An object that contains a set of functions that you can use to generate forces for your particles. Each of the functions contained inside the <i>forces</i> object can be called to generate a force function (i.e. when called they return a function that you can use with your emitters or particles). * @type {{attractQuadratic_: Function, attractLinear_: Function, attractConstant_: Function, repelQuadratic_: Function, repelLinear_: Function, repelConstant_: Function, tangent_: Function}} */ this.forces = { /** * Generate a force function that causes the particles to be attracted to a target, in a way that's proportional to the distance squared * @param {{x:number, y:number}} attractorPosition The position of the target * @param {number} magnitude The overall strength of the force * @returns {Function} A function to use with the Emitter or Particle behaviors */ attractQuadratic_: function(attractorPosition, magnitude) { return function(x, y) { pos.x = x; pos.y = y; var diff = wade.vec2.sub(attractorPosition, pos); return wade.vec2.scale(diff, magnitude * wade.vec2.length(diff)); }; }, /** * Generate a force function that causes the particles to be attracted to a target, in a way that's proportional to the distance * @param {{x:number, y:number}} attractorPosition The position of the target * @param {number} magnitude The overall strength of the force * @returns {Function} A function to use with the Emitter or Particle behaviors */ attractLinear_ : function(attractorPosition, magnitude) { return function(x, y) { pos.x = x; pos.y = y; var diff = wade.vec2.sub(attractorPosition, pos); return wade.vec2.scale(diff, magnitude); }; }, /** * Generate a force function that causes the particles to be attracted to a target, with a constant strength * @param {{x:number, y:number}} attractorPosition The position of the target * @param {number} magnitude The overall strength of the force * @returns {Function} A function to use with the Emitter or Particle behaviors */ attractConstant_ : function(attractorPosition, magnitude) { return function(x, y) { pos.x = x; pos.y = y; var diff = wade.vec2.sub(attractorPosition, pos); return wade.vec2.scale(wade.vec2.normalizeIfPossible(diff), magnitude); }; }, /** * Generate a force function that causes the particles to be repelled by a target, in a way that's proportional to the distance squared * @param {{x:number, y:number}} repellerPosition The position of the target * @param {number} magnitude The overall strength of the force * @returns {Function} A function to use with the Emitter or Particle behaviors */ repelQuadratic_: function(repellerPosition, magnitude) { return function(x, y) { pos.x = x; pos.y = y; var diff = wade.vec2.sub(repellerPosition, pos); return wade.vec2.scale(diff, -magnitude * wade.vec2.length(diff)); }; }, /** * Generate a force function that causes the particles to be repelled by a target, in a way that's proportional to the distance * @param {{x:number, y:number}} repellerPosition The position of the target * @param {number} magnitude The overall strength of the force * @returns {Function} A function to use with the Emitter or Particle behaviors */ repelLinear_ : function(repellerPosition, magnitude) { return function(x, y) { pos.x = x; pos.y = y; var diff = wade.vec2.sub(repellerPosition, pos); return wade.vec2.scale(diff, -magnitude); }; }, /** * Generate a force function that causes the particles to be repelled by a target, with a constant strength * @param {{x:number, y:number}} repellerPosition The position of the target * @param {number} magnitude The overall strength of the force * @returns {Function} A function to use with the Emitter or Particle behaviors */ repelConstant_ : function(repellerPosition, magnitude) { return function(x, y) { pos.x = x; pos.y = y; var diff = wade.vec2.sub(repellerPosition, pos); return wade.vec2.scale(wade.vec2.normalizeIfPossible(diff), -magnitude); }; }, /** * Generate a force function that causes the particles to move along the tangent of a circle centered on the target position * @param {{x:number, y:number}} centerPosition the position of the target * @param {number} magnitude The overall strength of the force * @returns {Function} A function to use with the Emitter or Particle behaviors */ tangent_: function(centerPosition, magnitude) { return function(x, y) { pos.x = x; pos.y = y; var diff = wade.vec2.sub(centerPosition, pos); return wade.vec2.scale({x: diff.y, y: -diff.x}, magnitude); }; } };