I want to be able to plot simple vectors (not vector fields or anything more fancy) in a 3d space, like some lines each with an arrow head that go from the origin of the plane to any other given point.

The simpler it is to use (input vectors, labels, customize, etc), the better, since this tool is intended to be used in education with a wide range of different ages and experience levels.

I've tried both Matlab and Octave, and through quiver3 I can do stuff like this:

plot vectors with octave example

Which is ok, but has some issues:

  1. With the flexibility of those tools there also comes some complexity cost. In some age ranges and some experience levels I'll lose too much time and attention using them.

  2. In Octave, I can't get the axis to pass through the origin! It can be done in Matlab with a 3rd party library, but Matlab is not cheap, and at this task plots end up looking like in Octave anyway.

I'm looking for something more simple to customize and work with, I don't need it to be flexible (I'm looking for very simple customizations, like axis positions, labels, colors, etc), it doesn't need to perform calculations (only need the plot).

As an example, I use Desmos graphic calculator for 2d stuff all the time, while not as flexible and powerful as the above programs, it's very easy to do 2d plots that look great with straight to the point customization.

So, which are the options/alternatives? What do you use for school, work, lab?

2 Answers 2



Screenshot of GeoGebra showing the 3D Graphics pane and an already entered vector a = (1, 2, 3)^T. In addition, the input bar contains "b = Vector((-3, 2, 1))", which the 3D Graphics pane already previews. The vector "a" is also painted in green and dashed line style.

Your requirements:

  • Easy to use, I obtained the above screenshot by these steps:

    • Start GeoGebra
    • Select in the menu View -> 3D Graphics
    • In the input bar, enter a = Vector((1, 2, 3)), for example.
      You can also enter a start and end point for direction vectors: Vector((1, 1, 1), (2, 2, 2)). Generally, entering any command already shows its possible parameters and overloads in an IntelliSense fashion.
  • Labels can be created using the Text command: Text("hello", (1,1,1)).
    Be sure to then right-click the object in the Algebra Pane, select Properties, Advanced and activate "3D Graphics" under Location as per this answer.

  • Customizable: by right-clicking any object and selecting Properties, you can change numerous settings, e.g. the font size, color and line style.

General Features:

  • Gratis and open source
  • Cross-platform
  • Can export images to various formats (PNG, EPS and some LaTeX code)
  • Has JavaScript scripting support.
  • Supports "variable sliders" (think of <input type="range"> if you know HTML) and their animation/automatic increase & decrease mode every XX milliseconds.

I use it regularly for quickly sketching things or gaining an intuitive understanding of some formulae. One downside, however, is that the input bar/CAS system is sometimes unable to understand some deeply nested commands which are dependent on slider variables.


This sort of thing is ideal to do with python and one of the plotting libraries such as MatPlotLib or Bokeh.

  • Free, Gratis & Open Source
  • Cross Platform (will run on Windows, Mac or Linux and on a RaspberryPi upwards)
  • Small installation
  • Flexible
  • Plots can be saved and exported as pdf files or images.

A couple of the MatPlotLib examples from the 3D plotting gallery:

3D Quiver:

3D quiver plot

Demonstrates plotting directional arrows at points on a 3d meshgrid.

from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure()
ax = fig.gca(projection='3d')

# Make the grid
x, y, z = np.meshgrid(np.arange(-0.8, 1, 0.2),
                      np.arange(-0.8, 1, 0.2),
                      np.arange(-0.8, 1, 0.8))

# Make the direction data for the arrows
u = np.sin(np.pi * x) * np.cos(np.pi * y) * np.cos(np.pi * z)
v = -np.cos(np.pi * x) * np.sin(np.pi * y) * np.cos(np.pi * z)
w = (np.sqrt(2.0 / 3.0) * np.cos(np.pi * x) * np.cos(np.pi * y) *
     np.sin(np.pi * z))

ax.quiver(x, y, z, u, v, w, length=0.1, normalize=True)


Gives: enter image description here

3D Text

Text annotations in 3D

Demonstrates the placement of text annotations on a 3D plot.

Functionality shown:
- Using the text function with three types of 'zdir' values: None,
  an axis name (ex. 'x'), or a direction tuple (ex. (1, 1, 0)).
- Using the text function with the color keyword.
- Using the text2D function to place text on a fixed position on the ax object.

from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.gca(projection='3d')

# Demo 1: zdir
zdirs = (None, 'x', 'y', 'z', (1, 1, 0), (1, 1, 1))
xs = (1, 4, 4, 9, 4, 1)
ys = (2, 5, 8, 10, 1, 2)
zs = (10, 3, 8, 9, 1, 8)

for zdir, x, y, z in zip(zdirs, xs, ys, zs):
    label = '(%d, %d, %d), dir=%s' % (x, y, z, zdir)
    ax.text(x, y, z, label, zdir)

# Demo 2: color
ax.text(9, 0, 0, "red", color='red')

# Demo 3: text2D
# Placement 0, 0 would be the bottom left, 1, 1 would be the top right.
ax.text2D(0.05, 0.95, "2D Text", transform=ax.transAxes)

# Tweaking display region and labels
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)
ax.set_zlim(0, 10)
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')


Gives:enter image description here

I would also strongly suggest taking a look at Jupyter + the VPython kernel as this opens up more possibilities:

  • Free, Gratis & Open Source
  • Cross Platform (runs on Windows, OS-X & Linux)
  • Interface via the web browser
  • Much more interactive
  • Animation
  • More visually grasping

I would also suggest taking a look at the GlowScript site which makes extensive use of VPython: enter image description here

GlowScript 2.6 VPython

# Written by Bruce Sherwood, licensed under Creative Commons 4.0.
# All uses permitted, but you must not claim that you wrote it, and
# you must include this license information in any copies you make.
# For details see http://creativecommons.org/licenses/by/4.0

# Angular momentum of a binary star
scene.background = color.white
scene.y = 0
scene.width = 600
scene.height = 600
G = 6.7e-11
d = 1.5e11
star1 = sphere(pos=vector(d,0,0), radius=5e9, color=color.magenta, make_trail=True, retain=200, interval=10)
star1.mass = 1e30
star2 = sphere(pos=vector(0,0,0), radius=1e10, color=color.blue, make_trail=True, retain=200, interval=10)
star2.mass = 2*star1.mass

# make elliptical orbit in xz plane
ev = (2*pi*d/(365*24*60*60)) 
star1.p = vector(0, star1.mass*ev, 0)
star2.p = -star1.p
dt = 12*60*60

scene.center = vector(-0.2*d,0.3*d,0)
scene.range = 1.8*d # set size of window in meters
scene.forward = vector(0,1,-1) # tip camera angle
scene.lights = []

# A in the xz plane
locationA = vector(-0.4*d, 0, 0) 

Lscale = 3.7e-35
pscale = 4e-24
offset = 2*star1.radius
h = star1.radius
Larr1 = arrow(pos=locationA-vector(offset,0,0), shaftwidth=star1.radius, axis=vector(0,0,0), color=star1.color)
rarr1 = arrow(pos=locationA, shaftwidth=star1.radius, axis=vector(0,0,0), color=color.cyan)
parr1 = arrow(pos=star1.pos, shaftwidth=star1.radius, axis=vector(0,0,0), color=color.red)
Larr2 = arrow(pos=locationA+vector(offset,0,0), shaftwidth=star1.radius, axis=vector(0,0,0), color=star2.color)
rarr2 = arrow(pos=locationA, shaftwidth=star1.radius, axis=vector(0,0,0), color=color.cyan)
parr2 = arrow(pos=star2.pos, shaftwidth=star1.radius, axis=vector(0,0,0), color=color.red)
Larr = arrow(pos=locationA, shaftwidth=star1.radius, axis=vector(0,0,0), color=vector(.7,.5,0))
Llabel1 = label(pos=locationA, text='L1', box=0, opacity=0, color=color.black)
Llabel2 = label(pos=locationA, text='L2', box=0, opacity=0, color=color.black)
Llabel = label(pos=locationA, text='L1+L2', box=0, opacity=0, color=color.black)

while True:
    r = star2.pos-star1.pos
    F = -(G*star2.mass*star1.mass/mag(r)**2)*norm(r)
    star2.p = star2.p + F*dt
    star2.pos = star2.pos + (star2.p/star2.mass)*dt
    star1.p = star1.p - F*dt
    star1.pos = star1.pos + (star1.p/star1.mass)*dt
    parr2.pos = star2.pos
    parr2.axis = star2.p*pscale
    parr1.pos = star1.pos
    parr1.axis = star1.p*pscale
    rA1 = star1.pos-locationA
    rarr1.axis = rA1
    L1 = cross(rA1,star1.p)
    Larr1.axis = L1*Lscale
    rA2 = star2.pos-locationA
    rarr2.axis = rA2
    L2 = cross(rA2,star2.p)
    Larr2.axis = L2*Lscale
    Larr.axis = (L1+L2)*Lscale
    Llabel1.pos = Larr1.pos+Larr1.axis+vector(0,h,0)
    Llabel2.pos = Larr2.pos+Larr2.axis+vector(0,h,0)
    Llabel.pos = Larr.pos+Larr.axis+vector(0,h,0)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.