Desmos is a wonderful user-friendly calculator for graphing in two dimensions. However, creating three-dimensional projects can be confusing at first, but more importantly they are slow due to essentially having to recompute meshes and projections on every frame.
Three.js is an excellent rendering library that simplifies much of the process of WebGL and web shaders.
The goal of this project is to unite the two:
This project is unofficial, so it may break at any time due to changes in the Desmos code.
DesThree.user.js
in the latest Release, then hit Install.
Please report any bugs here on the Github issues tracker or discuss on the Unofficial Desmos Discord in #programming.
npm install
npm run dev
dist/DesThree-dev.user.js
into TamperMonkeydist/DesThree-dev.user.js
loads dist/DesThree.user.js
through file URLUse npm run lint
or npm run lint-fix
before a commit to adhere to Standard style. You may also want the packages linter-js-standard
and atom-standard-formatter
for convenience, though npm run lint-fix
is more thorough than whatever atom-standard-formatter
does.
To test a production build:
npm run build-tests
and paste dist/DesThree-test.user.js
into a new TamperMonkey scriptnpm run build
and paste dist/DesThree.user.js
into a TamperMonkey scriptnpm run test
, which lints and opens https://www.desmos.com/calculator?testDesThree=0. If this doesn't work, open the URL manually.Periodically, update test definitions based on Chrome's code coverage tool.
To build once for production, use npm run build
instead of npm run dev
. The built script should be available in dist/DesThree.user.js
, which can be pasted directly into TamperMonkey.
Other release details are available in RELEASE.md
DesThree will automatically load in every graph you load with the userscript enabled, and at present, it completely covers the normal 2D graph.
To create a DesThree expression, insert it from the ➕ menu in the top-left as you might insert a folder or an image. You can also type @3
at the start of a normal expression to begin a DesThree expression; the icon to the left should switch to the shape of a cube.
If DesThree ever stops showing something or stops being responsive, save and reload the page. If it still doesn't work, it's probably the fault of your expressions.
If you ever can't type something (e.g. PointLight
automatically replaces int
with an integral), type it in some other tab/program and copy-paste into Desmos.
The simplest expression you can write simply sets the position of the camera as (x,y,z)=(1,2,5)
(https://www.desmos.com/calculator/i8min5dnfo):
pos = (1,2,5)
PerspectiveCamera(pos)
cameraPos
, camera_pos
, cube_1
, thr33js
12cube
, _moo
(0,0,0)
by default.Showing a cube may be more complicated than what you expect. The process goes as follows:
(0,0,0)
.These steps can be done as follows (https://www.desmos.com/calculator/6t1l6vhfiy):
pos = (1,2,5)
PerspectiveCamera(pos)
cubeGeometry = Box(1,1,1)
material = NormalMaterial()
mesh = Mesh(cubeGeometry, material)
Show(mesh)
Alternatively, you can place them all in one expression (https://www.desmos.com/calculator/0cjxlfabx5):
PerspectiveCamera((1,2,5))
Show(Mesh(Box(1,1,1), NormalMaterial()))
PerspectiveCamera
seems to have double parentheses, but the inner ones are for the point (1,2,5)
—only the outer ones are for the function call.Box
takes three arguments: width, height, length. To make a cube, we use the same value for all threeNormalMaterial
takes no arguments. Example 5 will go over the different kinds of materialsMesh
simply takes the geometry and material and arguments.Show
takes the mesh and shows it. This does not occur automatically because we sometimes want to store a mesh in a variable and not show it until after some transformationsA great part about using Desmos is that we can use sliders and math in the expressions.
To see an example, we will move the box around in 3D. We use the Position
function to create a new mesh that corresponds to the mesh translated (https://www.desmos.com/calculator/pygwi1lnpi):
PerspectiveCamera((1,2,5))
cubeGeometry = Box(1,1,1)
material = NormalMaterial()
mesh = Mesh(cubeGeometry, material)
x_0 = 0
y_0 = 0
z_0 = 0
meshp = Position(mesh, (x_0, y_0, z_0))
Show(meshp)
Alternatively, we can specify position directly in the Mesh
function (https://www.desmos.com/calculator/ir00nvxzxs):
meshp = Mesh(cubeGeometry, material, (x_0, y_0, z_0))
Show(meshp)
meshp
, the mesh
correctly positioned in 3D.To improve our future viewing, we will orbit the camera using equations of spherical coordinates.
This simply involves a few more equations instead of placing the camera at a fixed position (https://www.desmos.com/calculator/3cy48fxofl):
r_c = 5
theta_c = 0
phi_c = 0
pos = (r_c·cos(phi_c)cos(theta_c), r_c·sin(phi_c), r_c·cos(phi_c)sin(theta_c))
PerspectiveCamera(pos)
cubeGeometry = Box(1,1,1)
material = NormalMaterial()
mesh = Mesh(cubeGeometry, material)
x_0 = 0
y_0 = 0
z_0 = 0
meshp = Position(mesh, (x_0, y_0, z_0))
Show(meshp)
Remember how we're using NormalMaterial
above? That is the simplest material (and the default if none is given to the Mesh
function): there are no arguments, and we don't have to deal with lights. Each face is simply colored based on its normal vector.
Two other materials exist that are always visible regardless of lights:
DepthMaterial
: also takes no argumentsBasicMaterial
: takes a color as argument and always shows that color regardless of lightingOther materials exist for shading meshes, and these handle lights:
LambertMaterial
PhongMaterial
ToonMaterial
Each of these take color as their only argument.
Use them as follows:
material = LambertMaterial(RGB(10, 80, 180))
mesh = Mesh(geometry, material)
show(mesh)
With these last few materials, you need to add lights if you want to see anything.
Let's add a single point light at (-7, 6, 2)
with an intensity (brightness) of 1 (https://www.desmos.com/calculator/oznclpvvns):
Show(Position(PointLight(1), (-7, 6, 2)))
(Alternatively, you can use Show(PointLight(1, RGB(255,255,255), (-7,6,2)))
, which avoids the Position
function).
Looks good right? Yes, but try rotating to the back side (https://www.desmos.com/calculator/imbwxpqh1a). Ugh, that's too dark.
One solution would be to add more point lights so nothing can be dark, but that would get confusing and not look good. Instead, let's add an ambient light, which adds the intensity to every face (https://www.desmos.com/calculator/lsnje9ira5):
Show(AmbientLight(0.3))
0.01
(dark faces can be almost completely black—good for moody scenes) to 0.5
(all faces are easily visible)Anywhere a number can be accepted, a list of values can be accepted instead.
Let's make a sphere with radius 1
and place it at x-coordinates [-10, -5, 0, 5, 10]
(https://www.desmos.com/calculator/j4yf8gqofe):
geometry = Sphere(1)
mesh = Mesh(geometry, material)
meshp = Position(mesh, ([-10,-5,...,10], 0, 0))
Show(meshp)
Just like in vanilla Desmos, if several lists are passed to a function, a list is produced from applying the function to corresponding terms with the output length being the minimum of the lengths of the input lists (https://www.desmos.com/calculator/ebdz3ges1f):
L_x = [-10,-5,...,10]
mesh = Mesh(geometry, material)
meshp = Position(mesh, L_x, (0.05*L_x^2, 0))
Show(meshp)
You can even make a list of geometries with different parameters (https://www.desmos.com/calculator/zdy9szxjzy)
L_x = [-12.5,-7.5,...,12.5]
geometry = Sphere(0.02*L_x^2+0.5)
mesh = Mesh(geometry, material)
meshp = Position(mesh, L_x, (0.02*L_x^2, 0))
Show(meshp)
Go crazy, and have fun!
Property | Type | Default | Description |
---|---|---|---|
radius | number | Radius of circumscribing sphere | |
detail | number | 0 | If detail > 0 , the polyhedron becomes more rounded towards a sphere |
Property | Type | Default | Description |
---|---|---|---|
radius | number | Radius of circumscribing sphere | |
widthSegments | number | 16 | Greater = smoother |
heightSegments | number | 12 | Greater = smoother |
Property | Type | Default | Description |
---|---|---|---|
radius | number | Radius from center of torus to center of tube in cross section | |
tube | number | Radius of tube | |
radialSegments | number | 16 | Greater = smoother |
tubularSegments | number | 12 | Greater = smoother |
arc | number | 2π | Arc angle |
Property | Type | Default | Description |
---|---|---|---|
radius | number | ||
tube | number | Radius of tube | |
radialSegments | number | 8 | Greater = smoother |
tubularSegments | number | 64 | Greater = smoother |
p | number | 2 | How many times the geometry winds around its axis of rotational symmetry |
q | number | 3 | How many times the geometry winds around a circle in the interior of the torus |
Property | Type | Default | Description |
---|---|---|---|
radius | number | ||
height | number | ||
radialSegments | number | 16 | Greater = smoother |
heightSegments | number | 1 |
Property | Type | Default | Description |
---|---|---|---|
radiusTop | number | ||
radiusBottom | number | ||
height | number | ||
radialSegments | number | 16 | Greater = smoother |
heightSegments | number | 1 |
Just a convenience to avoid having to pass the same value for radiusTop
and radiusBottom
in Frustum
.
Property | Type | Default | Description |
---|---|---|---|
radius | number | ||
height | number | ||
radialSegments | number | 16 | Greater = smoother |
heightSegments | number | 1 |
A filled circle (flat). Lowering the value of the segments
argument would make a polygon.
Property | Type | Default | Description |
---|---|---|---|
radius | number | ||
thetaStart | number | 0 | |
thetaLength | number | 2π | Disc(1, 0, π) is a semi-circle |
segments | number | 16 | Disc(1, 0, 2π, 6) is a hexagon |
Also known as an annulus
Property | Type | Default | Description |
---|---|---|---|
innerRadius | number | ||
outerRadius | number | ||
thetaStart | number | 0 | |
thetaLength | number | 2π | |
thetaSegments | number | 16 | |
phiSegments | number | 1 |
A rectangular plane of finite size.
Property | Type | Default | Description |
---|---|---|---|
width | number | ||
height | number | ||
widthSegments | number | 1 | |
heightSegments | number | 1 |
Connects 2D points into a flat shape, assuming their y coordinate is 0.
Property | Type | Default | Description |
---|---|---|---|
points | list of Vector2s | ||
divisions | number | 1 | number of subdivisions to take of a spline passing through those points. If divisions = 1, then this a polygon between those points |
Connects 2D points into a polygon and extrudes to a specified depth
Property | Type | Default | Description |
---|---|---|---|
points | list of Vector2 | ||
divisions | number | 1 | number of subdivisions to take of a spline passing through those points. If divisions = 1, then this a polygon between those points |
depth | number | 10 | distance to extrude |
May also be known as "revolve."
Resolves (x,y)
points around the y-axis. The x
values are automatically absolute-valued.
Property | Type | Default | Description |
---|---|---|---|
points | list of Vector2s | ||
segments | number | 12 | greater = smoother |
phiStart | number | 0 | |
phiLength | number | 2π |
Creates a geometry directly from a list of faces. Most powerful method for creating a geometry.
Property | Type | Default | Description |
---|---|---|---|
faces | list of Face3s |
Property | Type | Default | Description |
---|---|---|---|
color | Color | white |
Always colored as the given color, ignoring lighting.
No arguments. Each face is colored based on its normal vector.
No arguments. Colors each point in gray-scale based on its depth from the camera. This is not very useful for general rendering and usually shows up mostly black unless the object is very close to the camera.
Property | Type | Default | Description |
---|---|---|---|
color | Color | white |
This uses Gourand shading, which calculates shading for each vertex and does linear interpolation to find pixel shading. As such, it only tends to look best for low-poly objects like cubes/boxes.
Property | Type | Default | Description |
---|---|---|---|
color | Color | white |
This uses Phong shading, which calculates shading on each pixel and additionally includes specular highlights (shiny surfaces). This tends to look better than MeshLambertMaterial for rounded objects like spheres but is slower.
Property | Type | Default | Description |
---|---|---|---|
color | Color | white |
Looks like a cartoon drawing.
Create an object from a geometry and material.
Property | Type | Default | Description |
---|---|---|---|
geometry | Geometry | ||
material | Material | NormalMaterial() |
|
position | Vector3 | (0,0,0) |
Draw a square grid of the given size
Property | Type | Default | Description |
---|---|---|---|
size | number | width, in world units | |
divisions | number | 10 | greater = more grid lines |
center lines color | Color | white | only applies if divisions is even |
grid color | Color | gray |
Draw a polar grid of the given size. The two colors alternate.
Property | Type | Default | Description |
---|---|---|---|
radius | number | ||
radials | number | 16 | number of radial lines to draw |
circles | number | 6 | number of circles to draw |
divisions | number | 64 | |
color1 | Color | white | |
color2 | Color | white |
Draw an arrow from tail with components given in vector.
Property | Type | Default | Description |
---|---|---|---|
tail | Vector3 | ||
vector | Vector3 | ||
color | Color | white | |
headLengthScale | number | 0.2 | length of the arrow head, as a multiplier of the length of the vector |
headWidthScale | number | 0.4 | diameter of the arrow head, as a multiplier of the length of the arrow head |
Draws standard x, y, z axes.
Property | Type | Default | Description |
---|---|---|---|
size | number | 1 |
Property | Type | Default | Description |
---|---|---|---|
intensity | number | 1 | Brightness |
color | Color | white | |
position | Vector3 | (0,0,0) |
Property | Type | Default | Description |
---|---|---|---|
intensity | number | 1 | Brightness |
color | Color | white |
Like AmbientLight, but the color changes from sky to ground
Property | Type | Default | Description |
---|---|---|---|
intensity | number | 1 | Brightness |
sky color | Color | white | |
ground color | Color | white | |
direction | Vector3 | down = (0, -1, 0) | direction vector from sky to ground |
Property | Type | Default | Description |
---|---|---|---|
intensity | number | 1 | Brightness |
color | Color | white | |
direction | Vector3 | down = (0, -1, 0) | direction of travel of light rays |
Like a PointLight except it emits light only within a cone from its position pointing towards a target. The angle and other settings of the cone can be controlled
Property | Type | Default | Description |
---|---|---|---|
position | Vector3 | ||
target | Vector3 | (0,0,0) | |
intensity | number | 1 | Brightness |
color | Color | white | |
angle | number | 60° = π/3 | |
penumbra | number | 0 | Takes values from 0 to 1. Percent of attenuation due to the penumbra effect (outer lighter part of the shadow) |
distanceLimit | number | 0 | Maximum distance of light (dims linearly). Set to 0 to allow infinite distance |
decay | number | 2 | Set to 2 to follow inverse-square low |
Property | Type | Default | Description |
---|---|---|---|
object | Object3D | Geometry not accepted | |
position | Vector3 | Vector by which to translate the object. |
Property | Type | Default | Description |
---|---|---|---|
position | Vector3 | Position of the camera | |
lookAt | Vector3 | (0,0,0) | Position of the point the camera looks at |
fov | number | 75 | field of view (degrees, planned: Change treatment based on radian mode of calculator) |
near | number | 0.1 | near clipping plane of camera frustum |
far | number | 1000 | far clipping plane of camera frustum |
Property | Type | Default | Description |
---|---|---|---|
object | Object3D |
Property | Type | Default | Description |
---|---|---|---|
near | number | ||
far | number | ||
color | Color |
Property | Type | Default | Description |
---|---|---|---|
density | number | ||
color | Color |
Property | Type | Default | Description |
---|---|---|---|
r | number | Red component, 0 to 255 | |
g | number | Green component, 0 to 255 | |
b | number | Blue component, 0 to 255 |
Please note that Desmos points (declared in vanilla Desmos expressions) are not currently supported.
Property | Type | Default | Description |
---|---|---|---|
x | number | ||
y | number |
Property | Type | Default | Description |
---|---|---|---|
x | number | ||
y | number | ||
z | number |
A collection of three vertices, used as an argument for BufferGeometry. Be careful with the order of the three vertices (clockwise or counterclockwise) because that affects computed face normals, which affects lighting
Property | Type | Default | Description |
---|---|---|---|
vertex a | Vector3 | ||
vertex b | Vector3 | ||
vertex c | Vector3 |