Tender is an experimental programming language specially designed for graphics, image processing, audio, scripting, and more! Here is a quick tutorial. Also check the docs!
Tender compiles into bytecode and executes on a stack-based virtual machine (VM) written in native Go. The language features a rich type system including matrix types for high-performance numerical computing, concurrency primitives with goroutines and channels, and a comprehensive standard library covering everything from graphics to networking.
Modern scripting often means installing dozens of packages before drawing a window, loading an image, or making a network request. Tender takes a different approach. It's perfect for building OpenGL (GL, GLUT, GLU, GLFW) prototypes instantly, without hours of build time.
- Simple — Readable syntax with familiar language features
- Single Binary — Compile once. Ship a single executable.
- Self-Sufficient — Graphics, OpenGL, audio, networking, image processing, compression, GUI, and more are included in the standard library.
- Simple and highly readable syntax
- Compiles to bytecode
- Supports rich built-in functions
- Includes an extensive standard library
- Designed for 2D graphics
- REPL (Read-Eval-Print Loop) for interactive development
- Rich type system including int, float, string, bool, char, null, big integers, big floats, complex numbers, bytes, arrays (dynamic and immutable), maps (dynamic and immutable), tuples, time values, error values, and matrices
- High-performance matrix types with support for int, float, and complex elements; element-wise and matrix multiplication; transposition; determinant; rank; trace; and diagonal operations
- Built-in concurrency with goroutines (
govm), channels (makechan), and synchronization primitives - User-defined structs with field types, nested structs, anonymous structs, and embedded fields
- Closures and first-class functions
- Template literals with
${}interpolation (similar to JavaScript template strings) - Advanced operators including pipe operators (
<|,|>), null coalescing (??), optional chaining (?.), ternary conditional (? :), compound assignment operators, and logical operators (&&,||) - Modular architecture with import statements, module aliasing, selective imports, embedded file import (
embed()), and file-based module loading - Runtime type introspection with
typeof()and type checking functions - Error handling through the
error()expression - Immutable data structures via
freeze()builtin - Loop control with
breakandcontinuestatements - For loops including traditional, for-in, conditional, and infinite loops
- Variable declarations with
varand constants withconst - Function definitions with
fnkeyword - Export statements for module exports
- Bytecode compilation with compilation, execution, and parse-only modes
- Comprehensive operator precedence matching conventional expectations
- Cross-platform support for Windows, macOS, Linux and Android (Termux)
- math: Mathematical constants and functions
- mathf: Unity-inspired math utilities for game development
- cmplx: Functions for complex numbers
- os: Platform-independent interface to OS functionality
- strings: String conversion, manipulation, and regular expressions
- times: Time-related functions
- rand: Random number generation
- fmt: Formatting functions
- json: JSON handling functions
- xml: XML handling functions
- base64: Base64 encoding and decoding
- hex: Hexadecimal encoding and decoding
- console: Functions to print colored text to the terminal
- gzip: Gzip compression and decompression
- zip: ZIP archive manipulation
- tar: TAR archive creation and reading
- bufio: Buffered I/O functions
- crypto: Cryptographic functions
- path: File path manipulation
- image: Image manipulation
- canvas: Drawing functions for canvases
- graphics: 2D graphics with OpenGL acceleration
- gl: OpenGL bindings for 3D graphics
- glu: OpenGL Utility Library bindings
- glut: OpenGL Utility Toolkit bindings
- glfw: GLFW window and input management
- dll: Dynamic link library interactions
- io: Input and output functions
- audio: Audio processing
- net: Networking functions
- http: HTTP client and server utilities
- websocket: WebSocket communication utilities
- gob: Gob Encoding/Decoding
- csv: CSV Encoding/Decoding
- wui: Native Windows GUI framework
- sync: Synchronization primitives
- Install Tender on your machine.
- Copy the sample code below:
// Canvas drawing exampleimport"canvas"ctx:=canvas.new_context(100, 100)
ctx.hex("#0f0") ctx.dash(4, 2) ctx.rect(25, 25, 50, 50) ctx.stroke()
ctx.save_png("out.png") - Save your code as
hello.td(use the.tdextension). - Run your script using the following command:
tender hello.td- Install the latest version of Go.
- Run the following command to install:
go install github.com/2dprototype/tender/cli/tender@latestPrecompiled binaries are available. Download them from the release tags.
Check the docs!
println("Hello, World!")import"graphics"win:=graphics.new_window(400, 400, "Tender")
win.on_draw(fn() {
win.clear("#000")
win.hex("#f00")
win.circle(200, 200, 100)
win.fill()
})
win.run()import"gl"import"glut"glut.init()
gl.init()
glut.init_display_mode(glut.RGBA|glut.DOUBLE|glut.DEPTH)
glut.init_window_size(400, 400)
glut.create_window("Tender OpenGL")
glut.display_func(fn() {
gl.clear(gl.COLOR_BUFFER_BIT)
gl.begin(gl.TRIANGLES)
gl.color3f(1, 0, 0)
gl.vertex2f(0, 0.8)
gl.color3f(0, 1, 0)
gl.vertex2f(-0.8, -0.8)
gl.color3f(0, 0, 1)
gl.vertex2f(0.8, -0.8)
gl.end()
glut.swap_buffers()
})
glut.main_loop()// Variable declarationsvarname="Tender"constPI=3.14159// Functionsfnadd(a, b) {
returna+b
}
// Closuresfnmake_counter() {
varcount=0returnfn() {
count++returncount
}
}
// Arrays and mapsvararr= [1, 2, 3, 4, 5]
varmap= { "key": "value" }
// Template literalsvaruser="John"vargreeting=`Hello ${user}, welcome to Tender!`println(greeting)
// StructstypePersonstruct {
namestringageint
}
varperson=Person{name: "John", age: 25}
person.age=26// Type conversion and checkingvarnum=int("123")
ifis_string(num) {
println("This is a string")
}
else {
println("This is not a string")
}
// Error handlingvarresult=error("something went wrong")
ifis_error(result) {
println(result.value)
}// Pipe operators for functional compositionvarresult= [1, 2, 3, 4, 6] |>sort|>reverse|>println// Null coalescingvarvalue=null ?? "default value"// Template literalsvaritems= ["apple", "banana", "orange"]
foriteminitems {
`Item: ${item}`|>println
}
// Optional chainingvaruser= {
profile: {
name: "jack"
}
}
varname= user?.profile?.namesysoutname, "\n"// Range generationvarnumbers=range(0, 10, 2) // [0, 2, 4, 6, 8]sysoutnumbers, "\n"// Module importsimport"math"asmvarsqrt2= m.sqrt(2)
println(sqrt2)Tender supports high-performance matrix types for numerical computing:
// Create matricesm1:=matrix([
[1, 2, 3], [4, 5, 6],
[4, 5, 6]
])
m2:=matrix([
[7, 8, 1], [9, 10, 2], [11, 12, 2]
])
// Matrix multiplicationm3:=m1*m2debug(m3)
// Element-wise operationsm4:=m1+10// Add scalar to every elementdebug(m4)
// Matrix propertiesrows:=m1.rowscols:=m1.colsshape:=m1.shapedebug(rows, cols, shape)
// Transposem1t:=m1.Tdebug(m1t)
// For square matricesdet:=m1.det// determinanttrace:=m1.trace// tracediag:=m1.diag// diagonal elementsdebug(det, trace, diag)
// Matrix methodsrow0:=m1.row(0) // Get first row as arraycol1:=m1.col(1) // Get second column as arrayflat:=m1.flatten// Flatten to arraydebug(row0, col1, flat)
// Type conversionm_int:=matrix(3, 3, "int", [1, 2, 3, 4, 5, 6, 7, 8, 9])
m_float:=m_int.to_float() // Convert to float matrixm_complex:=m_int.to_complex() // Convert to complex matrixdebug(m_float|>typeof, m_complex|>typeof)Tender provides built-in goroutines and channels for concurrent programming:
// ----- Simple goroutine -----g:=gofn() {
println("Hello from goroutine!")
return42// return a value
}()
// Wait for completion (blocks until done)g.wait()
// Get the return valueresult:=g.result()
println("Goroutine returned:", result) // 42// Abort a goroutine (if needed, e.g., from another goroutine)// g.abort()// ----- Channels -----ch:=chan(10) // buffered channel with capacity 10// Send and receivech<-"hello"msg:=<-chprintln(msg) // "hello"ch.close() // Close the channel (sends no more values)// ----- Concurrent worker pool -----fnworker(id, jobs, results) {
for {
job:=<-jobs// blocks until a value is availableifis_null(job) { // channel closed => receive returns nullbreak
}
results<-job*2// send result
}
}
jobs:=chan(100)
results:=chan(100)
// Start 10 workersfori:=0; i<10; i++ {
// govm(worker, i, jobs, resultsgoworker(i, jobs, results)
}
// Send 50 jobsfori:=0; i<50; i++ {
jobs<-i
}
jobs.close() // signal workers to stop// Collect results (order may vary)fori:=0; i<50; i++ {
res:=<-resultsprintln(res)
}Explore various examples demonstrating Tender's features in the examples directory.
Tender supports multiple operation modes:
# Start REPL (interactive mode)
tender
# Compile and run a source file
tender myapp.td
# Compile to bytecode
tender -o myapp myapp.td
# Run compiled bytecode
tender myapp
# Parse and output AST as JSON
tender -parse ast.json myapp.td
# Show version
tender -version
# or
tender -v
# Show help
tender -helpTender provides a rich type system with support for:
| Type | Description | Example |
|---|---|---|
int | 64-bit integer | 42 |
float | 64-bit floating point | 3.14159 |
bigint | Arbitrary-precision integer | bigint(12345678...) |
bigfloat | Arbitrary-precision float | bigfloat(3.1415...) |
complex | Complex number | 3+4i |
string | UTF-8 string | "hello" |
bool | Boolean | true or false |
char | Unicode character | 'a' |
bytes | Byte array | [72, 101, 108, 108, 111] |
array | Dynamic array | [1, 2, 3] |
immutable-array | Immutable array | [1, 2, 3] |
map | Dynamic map | {"key": value} |
immutable-map | Immutable map | {"key": value} |
tuple | Fixed-size immutable sequence | (1, "hello", true) |
matrix:int | Integer matrix | matrix(1,2,"int",[1,2]) |
matrix:float | Float matrix | matrix(1,2,"float",[1,2.2]) |
matrix:complex | Complex matrix | matrix(1,2,"complex",[1,2i]) |
struct | User-defined structure | user{name: "Alice", age: 30} |
time | Time value | time() |
error | Error value | error("message") |
null | Null value | null |
channel | Communication channel | chan(10) |
goroutine | Concurrent task handle | go fn(){ ... }() |
Syntax highlighting is currently available for:
- Notepad++: Download the configuration file here
- Support for additional editors coming soon
Tender is distributed under the MIT License, with additional licenses provided for third-party dependencies. See LICENSE_GOLANG and LICENSE_TENGO for more information.
Tender is written in Go, based on Tengo. We extend our gratitude to the contributors of Tengo for their valuable work.