Atlas
Introduction
This document describes the syntax and semantics of the Atlas programming language.
Atlas is a real-time visualization language focusing on ease of GPU utilization. The paradigm used by
Atlas is a stack of tensors. This offers advantages relating to memory management, making
copying, slicing, reversing, and transposing all constant time operations. The workhorse of Atlas is
the c (compute) statements, which allow the definition of arbitrary GLSL functions, multiple return values,
and parameters of different sizes. Compute statements always compute on the accelerator, and
consequently should be restricted to large tensors.
Basic Concepts
Atlas code is stack-oriented and uses a simple command-oriented syntax. Comments start with //
and extend to the end of the line. Whitespace and indentation are primarily for readability and do not
affect the program's semantics. All commands are terminated with semicolons (;).
Labels are declared with l and quotes such as l'aLabel'. The core point of this
language is to make it easier to program the GPU. Most commands consume their arguments; for example
the sequence 1;if'go'; would push a 1 onto the stack, and then immediately consume it as a
truth value and branch to the label 'go'.
Atlas runs natively on Windows 10 and 11, and also on the web using emscripten.
Program Model
The program model used by Atlas is a loop. At the end of the program running, the tensor on top of the stack is used as the RGB 3-channel display tensor, filling the window with that tensor, and then the program restarts from the beginning. The examples and documentation below will make this clearer.
Invocation
Atlas takes one parameter as an argument: the file to be run. All other parameters will be pushed as strings onto the stack. If not given a filename, Atlas will run "main.atl" if present, or print a brief usage message if not. To invoke through a browser copy Atlas.html, Atlas.js, Atlas.wasm and Atlas.data to a served directory. A file named filelist.txt must also be present in the directory, which is a list of files to preload onto the filesystem. The list will include a main.atl and any supporting files. See the docs directory for a concrete example.
Gotchas
When setting variables, the versions which take a number and set a uniform *write to different locations* than the non-uniform versions. Hence it's possible to reset a variable to zero by forgetting to include the numerical argument on a set command. GLSL names are mangled by replacing '.' with '_'. Because of this, and because OpenGL reserves variables containing __, you must not start glsl variable names with an underscore if in a workspace.
Syntactic Sugar
Syntactic sugar exists for getting and setting variables: get'foo' can be shortened to just foo, set'foo' can be shortened to just foo= and set'foo' N can likewise be shortened to foo= N.
Data Types
All data in Atlas is floating point. This means that linear indexing only works out to about 16 million (24 bits), and the effective maximum dimension size (but not total size) of a tensor is 16 million. The total number of elements may not exceed 2^32 (4 gigaelements) or addressing will fail. All other types of data (e.g. strings) are converted into a tensor of floating point values. Strings use backslash as the escape character so that '\'' is a string with a single quote and '\\' is a single backslash etc.
- Number: Integers and floats, scalars are tensors too (e.g.,
42,3.14). - String: Text codepoints in single quotes (e.g.,
'Hello'). - Image: Bitmaps may be loaded with the
img'image.bmp'command. - Tensor: A multi-dimensional array used for advanced data manipulation (e.g.
[[1 2] [3 4]]or[1.0 2.0]or just0.0).
Tensors
Tensors are central to Atlas, as they are the only data structure built into the language. A tensor is a generalized multi-dimensional array. For example, a vector is a 1D tensor, a matrix is a 2D tensor, and dimensions up to 4 are also supported.
Variables
Variables are set with a set command like set'varName' 2 with varName a valid GLSL
identifier. Variables set this way will be available as uniforms in shaders, as well as through
get'varName' commands.
Control Structures
Atlas uses labels and if statements for control flow. These two structures alone are sufficient
to reconstruct the rest. For example 1;if'label'; does an unconditional branch to l'label'
located elsewhere in the code.
Functions
Functions are just labels in Atlas followed by a return command. For example, to add the top two elements on the stack:
l'add'; // Add the top two tensors element-wise
+;return;
and then later in the code this function is called by name with just add.
Looping
Atlas programs are run in a loop, once per each frame of animation. Consequently, your program
should be designed to pass through rather than block. To quit the program, you can manually call
quit. At the end of each loop, a 4 channel rank 3 tensor must be on top of the stack, this
is used as the image to display.
Demos
A set of more full fledged demos can be seen in the distributed main.atl here.
Fibonacci Sequence
0;1; // Push the first two elements of the sequence.
l'fib';
1;dup;1;dup;+; // Duplicate the top two elements and then add them.
0;dup;21;-;if'fib'; // Loop back to l'fib' if the top of the stack isn't 21.
print;quit; // Show the results.
prints
CPU tensor 8
shape:
strides:
21.00
CPU tensor 7
shape:
strides:
13.00
... (truncated for brevity)
CPU tensor 0
shape:
strides:
0.00
Simple Gradient
size;if'skip'; // Skip if we've already created the gradient.
6;[16 16 3];c''''
ret[0]=i / 767.0;' 0 1 0 0; // The linear index goes to 16*16*3 - 1 = 767.0.
l'skip';
will display

additive
This command makes it so that subsequent c compute commands are done in additive mode. Additive mode means that if two triangles coincide, they both draw, and add together the results.
+ / - / * / / / % / ^ / sin / cos / floor / ceil / log / > / ==
These arithmetic commands take one or two arguments and do one of add, subtract, multiply, divide, modulus, exponentiate, sin, cosine, floor, ceiling, logarithm, compare greater than, or compare equals them, respectively. The comparison operators produce 1.0 for yes and 0.0 for no. The argument on the top of the stack is the subtrahend, denominator, or exponent, while the tensor below is the minuend, numerator, or base. These operations are done on the CPU and therefore should only be done on small tensors. Large tensor computation should be done with the c (compute) command. For example:
[1 2 3];[0.5 4 0.1];*;print;quit;
will print
CPU tensor 0
shape: 3
strides: 1
+----------------+
| 0.50 8.00 0.30 |
+----------------+
b (bury)
This command takes one scalar integer argument, and then buries the top tensor that deep into the stack. For example 1;2;3;4;1;bury;print;quit; would print
CPU tensor 3 ... This command necessitates a deep copy if the tensor to bury is a copy or slice of some other tensor.
backface
This command toggles backface culling for triangles generated by the vertex shader.
c (compute)
The c command is the core compute operation in Atlas. It lets you execute custom GLSL code on the GPU to produce an output tensor (or texture) from one or more input tensors.
Syntax
c'vertexShaderFuncs'optionalVertexShader'glslFuncs'glslExpression' argCount retCount channels reuse
1. Stack Preparation
Before calling c, you must have pushed the following onto the stack (in order, from top to bottom):
- Output Shape: A tensor that defines the shape of the output.
- Vertex Count: An integer specifying the vertex count (e.g.,
6for two triangles covering the tensor). - Input Tensors: (Optional) Up to four tensors that will serve as inputs to your shader.
- Return Tensors: (Optional) If
reuseis nonzero, these are the tensors that the results will be drawn on top of.
2. Quoted String Parameters
The command requires four quoted strings to define the shader logic:
vertexShaderFuncs: Declarations for functions or variables used in the vertex shader. Useful for passing data to the fragment shader.optionalVertexShader: The actual vertex shader code. If left empty, a default shader is used (generating two triangles).glslFuncs: Declarations for additional GLSL functions, constants, or helper code for the fragment stage.glslExpression: The main shader code. You must assign values to the output array elementsret[0]throughret[retCount - 1]here.
GLSL Scope: Variables set with a numeric argument via the set command are available (with . replaced by _). You also have access to:
i: Linear integer index.t: Anivec4multidimensional tensor index.ifloat,tf: Floating-point versions ofiandt.
3. Numeric Parameters
Following the strings, four numeric parameters configure the operation:
- argCount (0-4): The number of input tensors to pop from the stack. Inside GLSL, these are accessed via functions
a(t),b(t),c(t), andd(t). - retCount: The number of values the shader outputs (assigned to
ret[]). - channels: Determines the output mode:
0: Tensor Mode. Outputs standard tensors.1, 4: Texture Mode (Float). Creates a 1 or 4-channel texture.10, 40: Texture Mode (Normalized UInt8). 10 is single channel, 40 is RGBA.
Note: Textures cannot be reshaped (transpose/reverse) and must be sampled using
af(uv),bf(uv), etc., in subsequent shaders. - reuse: If nonzero, the return values are drawn on top of existing tensors on the stack (which must match the expected dimensions).
Example
Compute the element-wise minimum and maximum of two tensors:
[[1 2][3 4]];[[2 1][5 3]]; // Push two tensors
6;[2 2]; // Vertex count 6, Output shape [2 2]
c'''' // No vertex/helper code
ret[0] = a(t) < b(t) ? a(t) : b(t);
ret[1] = a(t) < b(t) ? b(t) : a(t);
' 2 2 0 0; // 2 args, 2 returns, Tensor mode, No reuse
print;quit;
cat (concatenate)
Given a stack of t1, t2, and axis (a scalar), the command concatenates t2 onto t1 along axis axis. For example:
[[0 1][2 3]];[[4 5][6 7]];0;cat;[[0 1][2 3]];[[4 5][6 7]];1;cat;print;quit;
prints
CPU tensor 1 ...
cls (clear screen)
This command clears the output buffer.
continue
This command causes execution to flow to the top of the currently loaded program as if you had jumped to a label at the start of the executing program.
depth
This command toggles depth testing (32-bit).
dup (duplicate)
Given a scalar integer N on top of the stack, duplicates the Nth item on the stack and places it on top. For example:
[0 1 2];0;dup;print;quit;
prints
CPU tensor 1 ...
e (enclose)
This takes the tensor on top of the stack and raises its rank by 1 appending [1] onto its shape. For example:
.5;e;[[0 1][2 3]];e;print;quit;
prints
CPU tensor 1 ...
ext (extrude)
This takes the tensor on top of the stack and raises its rank by 1 prepending [1] onto its shape. For example:
.5;ext;[[0 1][2 3]];ext;print;quit;
prints
CPU tensor 1 ...
eval
This takes one string argument, an atlas program to be run immediately, not in a loop. When running, this program has access to the invoking program's variables and stack. Variables declared only within an eval statement cannot be accessed outside it; they must be pre-declared in the containing script to be shared. Also note that you cannot call into an outside label from within an eval statement: you must include all relevant code using the include command within the evaluated expression.
first / last
These commands return the first or last element along the first axis, reducing rank by 1. For example:
[[0 1][2 3]];first;print;last;print;quit;
prints
CPU tensor 0 ...
gamepad
This command pushes a vector of vectors corresponding to the gamepad state of attached controllers. The returned vectors are of the form [0:throttleLeft 1:throttleRight 2:leftStickX 3:leftStickY 4:rightStickX 5:rightStickY 6:leftShoulder 7:rightShoulder 8:home 9:up 10:right 11:down 12:left 13:select 14:start 15:a 16:b 17:x 18:y 19:leftStick 10:rightStick]
gamepadRumble
This command takes one argument, a vector like [lowFrequencyRumble highFrequencyRumble duration index], and makes the gamepad with that index vibrate with those frequencies for that duration (in seconds). The frequencies are between 0.0 and 1.0.
get
Gets a named variable and pushes it onto the stack as a tensor. For example
get'vec';
would get a variable named vec and push it onto the stack. The variable name may be used directly: instead of using get'foo' you can just use foo.
gltf
The gltf command takes one immediate string parameter, like
gltf'filename.glb', and produces 5 tensors; the vertex data [vertexCount 22] (pos vec4, normal vec3, uv vec2, bones vec4, weights vec4, mat float, tangent vec4) , the index data [indexCount], the bones x keyframes x animations data [4 bones*anims frames 4], and the texture array [maxWidth maxHeight materialCount*2 4], which has rgba in one texture, and xy normal + 6bitsroughness+2bits metallness and baked AO in alpha in the other. The last tensor is a scalar, the animation count, so that the correct position in the bones tensor can be deduced.
if / ifn
The two if and ifn commands implement conditional branching:
get'bool';first;if'jump'; // This branches to the label l'jump' if 'bool' is positive.
0;ifn'jump'; // This unconditionally jumps to l'jump' because a 0 was pushed on the stack and ifn branches on zero.
Looks at the top of the stack, and jumps to the label 'jump' if it is/isn't positive. The top of
the stack must be a scalar and is consumed by the if/ifn command.
img (image)
Loads a 4-channel bitmap from a file. For example:
img'font.bmp'
loads font.bmp as a [width height 4] tensor.
include
This includes another file textually, placing all the commands in that file at the location of the include command. For example include'head.atl' would include all the commands of head.atl.
index
Selects elements from a tensor along a specified axis using a vector of indices. The stack should contain (from bottom to top): the source tensor, a vector of indices to select, and a scalar specifying the axis. The result is a new tensor containing only the selected slices along that axis.
For example, to select specific rows (axis 0) from a matrix:
[[10 11 12]
[20 21 22]
[30 31 32]
[40 41 42]]; // Source tensor (4x3 matrix)
[0 2 3]; // Indices: select rows 0, 2, and 3
0; // Axis 0 (rows)
index;
print;quit;
This produces a 3x3 matrix containing rows 0, 2, and 3:
[[10 11 12]
[30 31 32]
[40 41 42]]
To select specific columns (axis 1) instead:
[[10 11 12 13]
[20 21 22 23]]; // Source tensor (2x4 matrix)
[1 3]; // Indices: select columns 1 and 3
1; // Axis 1 (columns)
index;
print;quit;
This produces a 2x2 matrix containing columns 1 and 3:
[[11 13]
[21 23]]
Indices may be repeated or reordered to duplicate or shuffle elements:
[100 200 300]; // Source vector
[2 0 0 1]; // Indices: element 2, then 0 twice, then 1
0; // Axis 0
index;
print;quit;
This produces:
[300 100 100 200]
input
This loads a 6 element input tensor corresponding to the mouse cursor. The first two elements are the x and y mouse deltas, and the 3rd element is the mouse wheel delta. The next 3 elements correspond to the left, right, and middle mouse buttons, 1.0 if held, 0.0 if released, and 2.0 if double clicked.
keys
This command loads a [512] shaped tensor corresponding to keyboard presses. The array will have a 1 if the corresponding key is being pressed, and 0 if not. For example:
keys;[41 42 0];s;first;ifn'go';quit;l'go';
will exit if esc (SDL_Scancode 41) is pressed.
See SDL_Scancode docs.
kettle
This command takes one string argument and one numeric parameter, and saves that many elements off the stack into the filename provided, in a format that is optimized for loading back into the GPU. You set up the stack with the textures and tensors you want to save, kettle them, and then can quickload them later with unkettle. "cooking".
l (length)
This command takes a tensor of rank 1, a vector, and pushes the Euclidean distance of it onto the stack.
load
Loads another Atlas file, resets the stack, and starts executing it. For example: load'mandelbrot.atl'. If called without quotes, it treats the top of the stack (which must be a vector) as the filename.
m (matrix multiply)
This multiplies the top two tensors (which must be rank 2 or less) and pushes the result on the stack. The result is always of rank 2. For example [[1 0 1][2 1 1][0 1 1][1 1 2]];[[1 2 1][2 3 1][4 2 2]];m;print;quit; prints
CPU tensor 0 ...
minmax (minimum and maximum)
This takes the tensor on top of the stack and replaces it by a vector of length two with the minimum and maximum values occuring in the tensor, like [min max].
ortho (orthographic)
This takes a 6-vector argument in the form [left right bottom top near far] and pushes a corresponding orthographic projection matrix onto the stack.
pop
Pops the stack, doing nothing if the stack is already empty (not an error).
proj (projection)
This takes a 5-vector argument in the form [fov width height near far] and pushes a corresponding projection matrix onto the stack.
Prints the stack.
[1 2 .3];'Hello, world!';print;
printLine
Prints the string on top of the stack, newline included.
printString
Prints the string on top of the stack, newline not included.
quit
Quits the program.
r (reverse)
Reverses a tensor along a specified dimension (axis). The axis, a scalar, should be on top of the stack, and the tensor below that. This is equivalent to mirroring along an axis. For example:
[[0 1][2 3]];0;r;print;quit;
prints
CPU tensor 0 ...
raise
Given a scalar n, this raises the nth item on the stack to the top of the stack.
rep (repeat)
Given a scalar and tensor on top of the stack, this command repeats the tensor a number of times equal to the scalar. For example:
[0 1 2];3;rep;print;quit;
rep (repeat)
Given a shape and tensor on top of the stack, this command changes the shape of the tensor. The new old and old sizes must match, or an error occurs.
return
Returns from a function to the place that called that label:
1;if'past';l'square'; // Square a scalar on the top of the stack
0;dup;*;return;l'past';2;square;square;print;quit;
rot (rotate)
This function returns a 4x4 rotation matrix. The rotation is performed around a 3D vector (taken from the top of the stack) by an angle (stored as the scalar immediately below the vector on the stack).
s (slice)
Extracts a slice from a tensor along a given axis. The arguments are given as a rank 1 tensor (array)
in the form [start end axis], and will slice the tensor below it from start (inclusive)
to end (exclusive) along dimension axis.
set
Sets a named variable to a certain value. The size may be explicitly set, as the variables can be used as uniforms for the shaders. For example
[1 2 3];set'vec' 3;
would set a variable named vec with the value [1 2 3], and
make it available as a vec3 in compute shaders. The valid sizes are 1,2,3,4 for vectors, and 16
for a 4x4 matrix. If set is called without a size, the named variable will not be set as a uniform. This function necessitates a deep copy, and should be used sparingly on large tensors. A short form of this command exists; foo= or foo= N may be used instead of set'foo' or set'foo' N
sort
This commands takes 1 vector and produces a new vector of equal length containing the indices of that vector in sorted order, from smallest to largest.
shape
This returns a vector containing the shape of a tensor, if called twice it will return the rank of a tensor as a vector.
size
This pushes the size of the stack onto the top of the stack.
t (transpose)
Transposes two dimensions in a tensor. The argument is a rank 1 tensor (array) in the form
[axis1 axis2]. The tensor below that has those axes swapped.
textBufferView
This command creates a view of the standard output buffer. It takes one argument, a 3 vector like [width height scrollUp] where scrollUp causes that many visible lines to be skipped, scrolling the buffer up. Words are wrapped to width. This command produces a tensor suitable for use with std.textAreaToFlatTexture or std.textToTexture.
textInput
This command pushes the text input since the last call to this command onto the stack as ascii. This makes it easy to gather text from the keyboard. Up to 65536 characters are buffered.
texture
This command takes the texture on top of the stack, which must be rank 3, 1 or 4 component like [512 512 4] or [256 256 1], and makes it suitable for use as a texture. That is, this command enables and generates mipmaps and anisotropic filtering.
textureArray
This command takes the texture on top of the stack, which must be rank 4, 1 or 4 component like [512 512 512 4] or [256 32 256 1], and turns it into a 2d texture array. These textures can be sampled from using the overloaded af,bf,cf,df( vec3 ) versions. This command takes a single numeric argument, channels, which matches the c compute command: 1 for a single channel float, 10 for a single channel u8 normalized, 4 for an float vec4, and 40 for the rgba u8 version. This command does not turn on mipmapping and anisotropic filtering for the texture, that can be done by then calling the texture command.
timeDelta
This command pushes the frame time, in seconds, onto the stack as a scalar.
time
This command pushes a 2 vector with the the total time in hours as an integer, and the time since last hour in seconds. This provides accurate runtimes for up to 1875 years.
toString
This command takes the top of the stack, which must be a scalar, and returns it as a string for display.
translate
This command pushes a 4x4 translation matrix onto the stack, corresponding to a translation by the 3-vector argument on the stack.
unext (unextrude)
This command performs the reverse of extrusion on the tensor on top of the stack. That is, if the last dimension is of length 1, this command reduces the rank by 1. If the last dimension isn't of length 1, an error is generated.
unkettle
This command takes one string argument, a filename, and loads the kettled tensors and textures stored in it. It is reentrant, it returns on top of the stack a scalar indicating progress. When this reaches 0, it indicates the items have been pushed onto the stack. The filename is only consumed on the first call to unkettle, subsequent calls don't expect it until until progress reaches 0. The returned progress will be a number counting down from 2.0 to 1.0, and then jumps to 0 at completion, so that values close to 1.0 don't get confused with 0.0.
while / for
To do a while loop until i is 5, for example, you can do the following:
[0];set'i' 1; // Set i to 0
l'start'; // The start label
get'i';print;[1];+; // Get i onto the stack and increment it
0;dup;set'i' 1; // Set i to new value, leave i on stack
[6];-;first;if'start'; // Loop if i != 6
quit;
windowSize
This command pushes a 2-vector onto the stack with the width and height of the display window.
workspace
This command takes one immediate string parameter and sets the current workspace for the current file. For example, workspace'foo' will cause all subsequent sets and gets to be prefixed by "foo." and all glsl variables will be prefixed by "foo_". Because both sets and gets are prefixed, the current file need not prefix normal gets, just the glsl variables must always be prefixed as they are statically compiled at script inititialization. All subsequent commands are prefixed this way until the end of file or next workspace command.
AI Credits
Many AIs participated in the creation of Atlas. Special thanks to the frontier models that assisted with logic, debugging, and documentation over the last two years, including:
- OpenAI: GPT-4, GPT-4o, o1, o3, GPT-5, and GPT-5.1
- Anthropic: Claude 3.5 Sonnet, Claude Opus 4.1, Claude Sonnet 4.5, and Claude Opus 4.5
- Google: Gemini 2.5 Pro and Gemini 3 Pro