Quiver Plots in JavaScript

How to make a quiver plot in JavaScript. Quiver plots show a 2D vector field as an array of arrows.


Plotly Studio: Transform any dataset into an interactive data application in minutes with AI. Try Plotly Studio now.

The quiver trace type, new in Plotly.js v4, visualizes a 2D vector field as an array of arrows. A quiver trace takes four arrays of the same length: x and y give the position of each arrow, and u and v give the vector components at that position. Arrow direction and length come from (u, v).

Arrow placement and scaling are controlled by:

  • anchor — which part of the arrow sits at (x, y): 'tail' (the default), 'tip', or 'center'.
  • lengthmode'scaled' (the default) normalizes arrow lengths against the largest vector and the density of points, so a dense field stays readable; 'raw' draws each arrow at its own magnitude.
  • lengthfactor — a multiplier applied after lengthmode scaling. Values below 1 shorten every arrow, values above 1 lengthen them.
  • arrowref — how u and v are read. With 'data' (the default) they are data coordinates, so the drawn angle changes when you zoom one axis more than the other. With 'paper' they are pixel coordinates, so the angle is fixed. 'paper' always scales arrow lengths, and lengthmode: 'raw' is ignored.

Arrow lines are styled with marker.line.width and marker.line.dash, and the arrowhead is scaled relative to the line width by marker.arrowsize. Quiver traces also support box and lasso selection through the standard selected / unselected marker attributes.

Each arrow is anchored at an (x, y) position and points along the (u, v) vector given for that position.

var data = [{
  type: 'quiver',
  x: [0, 1, 2, 0, 1, 2],
  y: [0, 0, 0, 1, 1, 1],
  u: [1, 0.5, 0, 0.5, 0, -0.5],
  v: [0, 0.5, 1, 0.5, 1, 0.5]
}];

var layout = {
  title: {text: 'Basic Quiver Plot'},
  xaxis: {title: {text: 'x'}},
  yaxis: {title: {text: 'y'}},
  width: 600,
  height: 400
};

Plotly.newPlot('myDiv', data, layout);

This example plots the rotational field (u, v) = (-y, x) on a grid. anchor: 'center' centers each arrow on its grid point instead of starting it there, and lengthfactor scales every arrow by the same factor. With the default lengthmode: 'scaled', arrow lengths are normalized against the longest vector in the field, so the grid stays legible however large the underlying values are.

var x = [];
var y = [];
var u = [];
var v = [];

for (var i = -2; i <= 2; i++) {
  for (var j = -2; j <= 2; j++) {
    x.push(i);
    y.push(j);
    u.push(-j);
    v.push(i);
  }
}

var data = [{
  type: 'quiver',
  x: x,
  y: y,
  u: u,
  v: v,
  anchor: 'center',
  lengthfactor: 0.8,
  marker: {color: '#636efa'}
}];

var layout = {
  title: {text: 'Rotational Field, Arrows Centered on Each Point'},
  xaxis: {title: {text: 'x'}},
  yaxis: {title: {text: 'y'}, scaleanchor: 'x'},
  width: 600,
  height: 500
};

Plotly.newPlot('myDiv', data, layout);

Pass marker.color an array with one value per arrow, together with the usual colorscale attributes (marker.colorscale, marker.cmin, marker.cmax, marker.showscale, marker.colorbar), to color each arrow by that value. Here the arrows are colored by their own magnitude. If you enable a colorscale without supplying a marker.color array, arrows are colored by vector magnitude automatically. A single (non-array) marker.color paints the whole field one color.

var x = [];
var y = [];
var u = [];
var v = [];
var speed = [];

// A shear flow: horizontal velocity grows with y, with a small vertical component.
for (var i = 0; i <= 8; i++) {
  for (var j = 0; j <= 6; j++) {
    var ui = 0.2 + 0.25 * j;
    var vi = 0.4 * Math.sin(i / 2);
    x.push(i);
    y.push(j);
    u.push(ui);
    v.push(vi);
    speed.push(Math.sqrt(ui * ui + vi * vi));
  }
}

var data = [{
  type: 'quiver',
  x: x,
  y: y,
  u: u,
  v: v,
  marker: {
    color: speed,
    colorscale: 'Viridis',
    showscale: true,
    colorbar: {title: {text: 'speed'}},
    line: {width: 2}
  }
}];

var layout = {
  title: {text: 'Shear Flow, Arrows Colored by Speed'},
  xaxis: {title: {text: 'x'}},
  yaxis: {title: {text: 'y'}},
  width: 700,
  height: 500
};

Plotly.newPlot('myDiv', data, layout);

marker.line.width and marker.line.dash style the arrow shafts, and marker.arrowsize scales the arrowhead relative to the line width — the default of 1 draws a head about three times as wide as the shaft. This example overlays a measured field (solid) on a modelled one (dashed) so the two can be compared arrow for arrow.

var x = [];
var y = [];
var uModel = [];
var vModel = [];
var uMeasured = [];
var vMeasured = [];

for (var i = 0; i <= 5; i++) {
  for (var j = 0; j <= 5; j++) {
    x.push(i);
    y.push(j);
    uModel.push(1);
    vModel.push(0.15 * (j - 2.5));
    uMeasured.push(0.9);
    vMeasured.push(0.15 * (j - 2.5) + 0.25 * Math.cos(i));
  }
}

var data = [{
  type: 'quiver',
  name: 'model',
  x: x,
  y: y,
  u: uModel,
  v: vModel,
  marker: {
    color: '#7f7f7f',
    arrowsize: 0.8,
    line: {width: 2, dash: 'dot'}
  }
}, {
  type: 'quiver',
  name: 'measured',
  x: x,
  y: y,
  u: uMeasured,
  v: vMeasured,
  marker: {
    color: '#d62728',
    arrowsize: 1.2,
    line: {width: 3}
  }
}];

var layout = {
  title: {text: 'Modelled and Measured Fields'},
  xaxis: {title: {text: 'x'}},
  yaxis: {title: {text: 'y'}},
  showlegend: true,
  width: 700,
  height: 500
};

Plotly.newPlot('myDiv', data, layout);