Inkjet Trajectories

Quick Start

This is a basic simulation of inkjet drop trajectories from a moving head (or a moving substrate) and how they depend on velocity, drop size and working distance.


The app is based on core papers by Rodriguez-Rivero et. al. 1 and Ivan Arango et. al.2


Gap mm
Diameter μm
Drop Velocity m/s
Head Velocity → m/s
Density g/cc
Gravity ° (0=Down)
VFlow m/s
Relative Motion
Remove relative head
Output Data
//One universal basic required here to get things going once loaded
window.onload = function () {
    //restoreDefaultValues(); //Un-comment this if you want to start with defaults

//Main() is hard wired as THE place to start calculating when inputs change
//It does no calculations itself, it merely sets them up, sends off variables, gets results and, if necessary, plots them.
function Main() {
    //Save settings every time you calculate, so they're always ready on a reload

    //Send all the inputs as a structured object
    //If you need to convert to, say, SI units, do it here!
    const inputs = {
        H: sliders.SlideH.value * 1e-3, //mm to m
        D: sliders.SlideD.value * 1e-6, //μm to m
        DV: sliders.SlideDV.value,
        HV: sliders.SlideHV.value,
        Vflow: sliders.SlideVflow.value,
        rho: sliders.Sliderho.value * 1000, //g/cc to kg/m3
        theta: sliders.Slidetheta.value * Math.PI / 180, //deg to radians
        Invert: document.getElementById('Invert').checked,
        Remove: document.getElementById('Remove').checked,

    //Send inputs off to CalcIt where the names are instantly available
    //Get all the resonses as an object, result
    const result = CalcIt(inputs);
    document.getElementById('Data').value = result.Data;
    if (result.plots) {
        for (let i = 0; i < result.plots.length; i++) {
            plotIt(result.plots[i], result.canvas[i]);

    //You might have some other stuff to do here, but for most apps that's it for Main!

//Here's the app calculation
//The inputs are just the names provided - their order in the curly brackets is unimportant!
//By convention the input values are provided with the correct units within Main
function CalcIt({ H, D, DV, HV,Vflow, rho, theta ,Invert,Remove}) {
    const AF = 0.1806, BF = 0.6459, CF = 0.4251, DF = 6880.95 //For drag coeff calc
    const rhoair = 1.2 //kg/m3 of drop then air
    const g = 9.81, dt = 0.00002 //Timestep seems OK

    let t = 0, PH = 0, PV = H, VH = 0, VV = DV
    const r = D / 2
    const pl = 4 / 3 * Math.PI * Math.pow(r, 3) * 1e15
    const ReP = 2 * r / 1.8e-5 //Pre-reynolds number - Diameter/Air Viscosity
    const m = 4 / 3 * Math.PI * Math.pow(r, 3) * rho //Mass of the drop
    const mgh = m * g * Math.sin(theta), mgv = m * g * Math.cos(theta)
    const A = Math.PI * r * r, Afact = A / 2 * rhoair //Cross sectional area and drag factor

    let Dxy = [{ x: 0 * 1000, y: H * 1000 }], Hav = 0, HVnet,VVnet, Re
    if (Invert) {VH=HV;Hav=VH} else {Remove=false} //Don't allow Remove if not Invert
    //An alternative formula for CD is
    //CDv = 24 / Re * Math.pow(1 + 0.27 * Re, 0.43) + 0.47 * (1 - Math.exp(-0.04 * Math.pow(Re, 0.38)))
    //The results were found to be very similar

    while (t < 1 && PV > 0) {
        //Horizontal component
        HVnet = (Hav - VH)
        Re = Math.max(0.0001, Math.abs(ReP * HVnet))
        CDv = 24 / Re * (1 + AF * Math.pow(Re, BF)) + CF / (1 + DF / Re)
        FH = Afact * CDv * HVnet * HVnet + mgh
        dHdt = FH / m * dt
        VH += Math.sign(Hav) * dHdt
        PH += VH * dt
        //Vertical component
        Re = Math.max(0.0001, Math.abs(ReP * VV))
        CDv = 24 / Re * (1 + AF * Math.pow(Re, BF)) + CF / (1 + DF / Re)

        FV = -Afact * CDv * VVnet * VVnet + mgv
        dVdt = FV / m * dt
        VV += dVdt
        PV -= VV * dt
        t += dt
        if (Remove){
            Dxy.push({ x: (PH-HV*t) * 1000, y: PV * 1000 })
        } else {
            Dxy.push({ x: PH * 1000, y: PV * 1000 })
         if (Invert){
            Hav = Math.pow(PV / H, 1) * HV
        } else {
            Hav = Math.pow((H - PV) / H, 1) * HV
    const offs=Dxy[Dxy.length-1].x
    //Now set up all the graphing data detail by detail.
    let plotData = [Dxy], lineLabels = ["Drops"]

    const prmap = {
        plotData: plotData, //An array of 1 or more datasets
        lineLabels: lineLabels, //An array of labels for each dataset
        hideLegend: true, //Set to true if you don't want to see any labels/legnds.
        xLabel: 'x&mm', //Label for the x axis, with an & to separate the units
        yLabel: 'y&mm', //Label for the y axis, with an & to separate the units
        y2Label: null, //Label for the y2 axis, null if not needed
        yAxisL1R2: [], //Array to say which axis each dataset goes on. Blank=Left=1
        logX: false, //Is the x-axis in log form?
        xTicks: undefined, //We can define a tick function if we're being fancy
        logY: false, //Is the y-axis in log form?
        yTicks: undefined, //We can define a tick function if we're being fancy
        legendPosition: 'top', //Where we want the legend - top, bottom, left, right
        xMinMax: [0,], //Set min and max, e.g. [-10,100], leave one or both blank for auto
        yMinMax: [0,], //Set min and max, e.g. [-10,100], leave one or both blank for auto
        y2MinMax: [,], //Set min and max, e.g. [-10,100], leave one or both blank for auto
        xSigFigs: 'F0', //These are the sig figs for the Tooltip readout. A wide choice!
        ySigFigs: 'F0', //F for Fixed, P for Precision, E for exponential

    //Now we return everything - text boxes, plot and the name of the canvas, which is 'canvas' for a single plot
    return {
        Data: "Vol=" + pl.toFixed(0) + "pl, t=" + (t * 1e6).toFixed(0) + "μs, V=" + (VV.toFixed(1)) + "m/s, Offs="+offs.toPrecision(3)+"mm",
        plots: [prmap],
        canvas: ['canvas'],


An inkjet drop of diameter D exits a nozzle at velocity DV, printing across a gap H at a head velocity HV. The coordinate system defines the head vertically above the substrate, so gravity is set via an angle input (hint, head orientation makes almost no difference).

Although the head is moving relative to the substrate, it's more normal to think of a stationary head and a moving substrate.

The drop is slowed down by drag in the vertical direction and shifted horizontally by the Couette flow (linear increase in velocity) from the moving substrate. The final horizontal displacement is seen in the graph and the data output provides the time taken, the final (vertical) drop velocity and the volume of the drop for reference.

The calculation proceeds in time steps to determine the drag forces in each direction. For cross sectional area A in air of density ρ, velocity V, the Force, F is given by:


The acceleration of the particle of mass m (from `m=(4π)/3r^3ρ_"drop"`) is simply:


For each time step δt the velocity increases by `"δV"/"δt""δt"`.

Drag coefficient

The drag coefficient, CD, is often assumed to be the typical constant used for a sphere, 0.5. In fact it depends strongly on the Reynolds number and smaller particles experience much more relative drag than larger ones. For a particle of diameter D, velocity U in air with viscosity η, the Reynolds number is:


The drag coefficient implemented here depends on Re and four coefficients: A=0.1806, B=0.6459, C=0.4251, D=6880.95:


Other equations are available but give similar results over the relevant range.

Head versus Substrate motion

Some like to study a fixed head with a moving substrate (the default here because of the Rodriguez-Rivero approach) and others the "Inverted" option of moving head and fixed substrate. In the Inverted case, a drop that travelled "straight" would go in a diagonal line relative to the substrate so the plot is dominated by the relative head velocity. In Fig 9 of the Arango paper, a large 150pl drop travels in almost a straight line, while the 6pl drop is highly curved. If you select the "Remove relative head" option you can see the "real" flight of the drop. Both views have their uses.


If a vertical air flow can be created from the head down to substrate then the effective air resistance decreases and drop offset is reduced. This is one of many possible fixes for the problems caused by the head motion, but each "fix" adds extra problems as air flows interact in complex manners.


Rodriguez-Rivero, C., Castrejon-Pita, J.R., Hutchings, I.M., Aerodynamic effects in industrial inkjet printing. J. Imaging Sci. Technol. 59(4), 40401–1 (2015)


Ivan Arango, Leonardo Bonil, David Posada, Javier Arcila, Prediction of a flying droplet landing over a non‑flat substrates for ink‑jet applications, IJIDeM,2019, 13:967–980