DatumHue

A creative workstation

Chart your data, build games, teach, and sketch with code. One Lua API drives the whole canvas - charts, 3D, audio, physics, live shared documents. What you make is yours.

Open it in your browserTry the desktop freeRead the docs

Every pixel has source you can read

Flip between each picture and the program that made it.

Ask your data anything

Load a table, query it with SQL, and chart the answer.

Rendered output of the Ask your data anything sample (dark theme)Rendered output of the Ask your data anything sample (light theme)
local color = datumhue.color
local vec3 = datumhue.math.vec3

local commutes = [[
minutes,kind
4,walk
7,walk
9,walk
12,bike
14,bike
17,bike
19,bike
23,transit
28,transit
34,transit
41,transit
52,transit
]]

local rides = datumhue.bytes(commutes):csv()

local canvas = datumhue.draw.new({ width = 1200, height = 720 })
canvas:mount({ left = 40, top = 40, width = 1200, height = 720 })

local minutes = datumhue.chart.scale.linear({ domain = { min = 0, max = 60 } })
local chart = canvas:chart({
    pos = vec3(-560, -330, 0),
    width = 1120,
    height = 660,
    margin = { top = 52, right = 24, bottom = 44, left = 60 },
    x_scale = minutes,
})

local short = rides:query("SELECT minutes FROM data WHERE minutes < 20")
chart:histogram({
    data = short,
    column = "minutes",
    bins = 10,
    color = color("#83c092"),
    name = "short",
})
chart:axis({ scale = minutes, side = "bottom", grid = true })
chart:title({ text = "Commutes under 20 minutes", font_size = 26 })

Sculpt a living scene

A rippling surface and glowing materials, lit and animated in real time.

Rendered output of the Sculpt a living scene sample (dark theme)Rendered output of the Sculpt a living scene sample (light theme)
local color = datumhue.color
local vec3 = datumhue.math.vec3

local scene = datumhue.scene.new({ width = 1200, height = 720 })
scene.camera.pos = vec3(0, 2.6, 2.4)
scene.camera:look_at(vec3(0, 0, 0))

scene:directional_light({
    direction = vec3(-1, -0.3, 0),
    color = color("#ffffff"),
    intensity = 25000,
})

local water = scene:plane({
    subdivisions = 254,
    scale = vec3(4, 1, 4),
    color = color("#7fbbb3"),
})
water:deform({ kind = "ripple", amplitude = 0.08, frequency = 14, speed = 2 })

scene:sphere({
    pos = vec3(0, 0.7, 0),
    material = datumhue.material.standard({
        color = color("#2d353b"),
        emissive = color("#e69875"),
    }),
})

scene:mount({ left = 40, top = 40, width = 1200, height = 720 })

Paint with particles

Three drifting clouds of color, painted with a handful of lines.

Rendered output of the Paint with particles sample (dark theme)Rendered output of the Paint with particles sample (light theme)
local color = datumhue.color
local vec3 = datumhue.math.vec3

local scene = datumhue.scene.new({ width = 1200, height = 720 })
scene.camera.pos = vec3(0, 0, 9)
scene.camera:look_at(vec3(0, 0, 0))

local function cloud(x, tint, seed)
    local emitter = datumhue.particles.emitter(scene, {
        rate = 0,
        max_particles = 700,
        lifetime = 30,
        shape = { kind = "sphere", radius = 1.5 },
        velocity = { min = vec3(-0.05, -0.05, 0), max = vec3(0.05, 0.05, 0) },
        size = { start = 0.1, stop = 0.1 },
        color = { start = tint, stop = tint },
        pos = vec3(x, 0, 0),
        seed = seed,
    })
    emitter:burst(500)
end

cloud(-2.8, color("#e69875"), 7)
cloud(0, color("#a7c080"), 21)
cloud(2.8, color("#7fbbb3"), 42)

scene:mount({ left = 40, top = 40, width = 1200, height = 720 })

Code as a sketchbook

A phyllotaxis spiral from one loop -- the same math sunflowers use.

Rendered output of the Code as a sketchbook sample (dark theme)Rendered output of the Code as a sketchbook sample (light theme)
local color = datumhue.color
local vec3 = datumhue.math.vec3

local canvas = datumhue.draw.new({
    width = 1200,
    height = 720,
    background = color("#2d353b"),
})

local palette = {
    color("#a7c080"),
    color("#83c092"),
    color("#7fbbb3"),
    color("#dbbc7f"),
    color("#e69875"),
}

local golden = math.pi * (3 - math.sqrt(5))
for i = 1, 520 do
    local angle = i * golden
    local radius = 13.5 * math.sqrt(i)
    canvas:circle({
        pos = vec3(radius * math.cos(angle), radius * math.sin(angle), 1),
        radius = 2.5 + i * 0.012,
        color = palette[i % #palette + 1],
        filled = true,
    })
end

canvas:mount({ left = 40, top = 40, width = 1200, height = 720 })

Roll a dungeon, light a torch

Dice carve the rooms, A* plots the route, field-of-view casts the torchlight, and a dialogue box asks the question.

Rendered output of the Roll a dungeon, light a torch sample (dark theme)Rendered output of the Roll a dungeon, light a torch sample (light theme)
local color = datumhue.color
local vec3 = datumhue.math.vec3

local cols, rows, cell = 34, 14, 32
local map = datumhue.grid.new(cols, rows)
map:fill(1)

local dice = datumhue.random.new(77)
local rooms = {}
for _ = 1, 6 do
    local w, h = dice:int(4, 8), dice:int(3, 5)
    local x, y = dice:int(1, cols - w - 1), dice:int(1, rows - h - 1)
    map:fill_rect(x, y, w, h, 0)
    rooms[#rooms + 1] = { x = x + math.floor(w / 2), y = y + math.floor(h / 2) }
end
for i = 2, #rooms do
    local a, b = rooms[i - 1], rooms[i]
    map:fill_rect(math.min(a.x, b.x), a.y, math.abs(a.x - b.x) + 1, 1, 0)
    map:fill_rect(b.x, math.min(a.y, b.y), 1, math.abs(a.y - b.y) + 1, 0)
end

local hero, hoard = rooms[1], rooms[#rooms]
local lit = {}
for _, c in ipairs(map:field_of_view(hero.x, hero.y, { range = 7, opaque = { 1 } })) do
    lit[c.y * cols + c.x] = true
end
local path = map:find_path(hero.x, hero.y, hoard.x, hoard.y, { blocked = { 1 } })

local canvas = datumhue.draw.new({ width = 1200, height = 720, background = color("#232a2e") })
local ox, oy = -cols * cell / 2, 100 + rows * cell / 2
local function at(gx, gy, z)
    return vec3(ox + (gx + 0.5) * cell, oy - (gy + 0.5) * cell, z)
end
for y = 0, rows - 1 do
    for x = 0, cols - 1 do
        if map:get(x, y) == 0 then
            local tone = lit[y * cols + x] and "#4f585e" or "#2d353b"
            canvas:rect({
                pos = at(x, y, 0) - vec3(cell / 2 - 1, cell / 2 - 1, 0),
                width = cell - 2,
                height = cell - 2,
                color = color(tone),
            })
        end
    end
end
for _, c in ipairs(path) do
    canvas:circle({ pos = at(c.x, c.y, 1), radius = 3.5, color = color("#dbbc7f") })
end
canvas:circle({ pos = at(hero.x, hero.y, 2), radius = 9, color = color("#a7c080") })
canvas:rect({
    pos = at(hoard.x, hoard.y, 2) - vec3(8, 8, 0),
    width = 16,
    height = 16,
    color = color("#e69875"),
})

datumhue.dialogue.ask("The hoard sleeps past the last arch. Carry the torch in?", {
    { label = "Light it", value = true },
    { label = "Go in dark", value = false },
}, function(torch)
    datumhue.dialogue.say(torch and "The dark eats oil, not gold." or "Brave. Or blind.", "Keeper")
end, "Keeper")

local talk = datumhue.dialogue.current()
canvas:rect({ pos = vec3(-544, -300, 3), width = 1088, height = 132, color = color("#2d353b") })
canvas:text({
    text = talk.speaker,
    pos = vec3(-520, -186, 4),
    font_size = 20,
    color = color("#e69875"),
})
for i, line in ipairs(talk.lines) do
    canvas:text({
        text = line,
        pos = vec3(-520, -186 - i * 26, 4),
        font_size = 20,
        color = color("#d3c6aa"),
    })
end
for i, label in ipairs(talk.choices) do
    local picked = i == talk.selected
    canvas:text({
        text = (picked and "> " or "  ") .. label,
        pos = vec3(-520 + (i - 1) * 240, -264, 4),
        font_size = 20,
        color = picked and color("#a7c080") or color("#859289"),
    })
end

canvas:mount({ left = 40, top = 40, width = 1200, height = 720 })

Teach with pages that compute

A book page that mixes prose with a chart drawn by the very code it teaches.

Rendered output of the Teach with pages that compute sample (dark theme)Rendered output of the Teach with pages that compute sample (light theme)
local function harmonics(waves)
    local pts = {}
    for i = 0, 96 do
        local x, y = i / 96 * 4 * math.pi, 0
        for k = 1, waves * 2, 2 do
            y = y + math.sin(k * x) / k
        end
        pts[#pts + 1] = string.format("point x=%.2f y=%.3f", x, y)
    end
    return table.concat(pts, "\n")
end

local lesson = string.format(
    [[
# How a square wave hides in sines

Stack the odd harmonics of a sine wave - each one faster and fainter
than the last - and the sum starts to square off. Five terms in, the
corners are already showing.

```kdl
chart height=420 {
    line color="#859289" width=1 {
%s
    }
    line color="#7fbbb3" width=3 {
%s
    }
}
```

> This page is a program: both lines above were computed by the loop
> in its source. Raise `waves` to fifty and the corners turn sharp.
]],
    harmonics(1),
    harmonics(5)
)

datumhue.bytes(lesson):book():mount({ left = 40, top = 40, width = 1200, height = 720 })

Why DatumHue

One language, the whole canvas

Charts, 3D scenes, voxels, particles, audio, physics, UI, and shared documents share one consistent Lua API. Learn it once and every kind of creation composes the same way - a chart can live inside a game, a game inside a lesson.

Data that answers back

Open a table, query it with SQL, stream the answers into charts, and explore by panning and zooming. Big datasets stay smooth - the workstation streams and downsamples instead of making your script hold everything.

Offline, and yours

The Personal and Indie editions run with no account and no connection. Nothing you make is transmitted, stored, or observable by anyone but you.

Many apps, one calm desktop

Run several creations side by side. Each is sandboxed, fairly scheduled, and billed for its own time - one busy or waiting app can't freeze the rest, and every capability is granted per program.

A studio's toolbox included

Typed editor autocomplete for the whole API, offline docs, a test runner with coverage, a profiler, breakpoint debugging from your editor, and a live console.

Together, live

Creations can open shared documents: everyone in one sees changes as they happen. Losing your connection never stops you - your edits merge back in when you return.

Everything inside the workstation → DatumHue for organizations →

Pick how you own it

Every edition shares the same canvas and the same Lua API; they differ in what ships alongside the workstation and in what the license lets you do.

Personal

The desktop workstation, yours to keep.

$79 one-time

Free 14-day trial - no card, no account

Perpetual — yours to keep

For hobbyists and individuals creating for themselves.

One purchase, one person, the whole creative API - charts, 3D, particles, audio, UI, live documents - on a workstation that runs entirely offline. Every release published inside your five-year update window is yours to run forever. Try it free first: the trial is the full workstation, nothing held back.

What you get

  • The full desktop workstation, offline
  • Perpetual license - covered releases run forever
  • Five-year update window; extend it whenever you choose
  • Commercial use of what you make, at any revenue
  • All your machines, one person

Indie

Ship what you build.

$199 one-time

One license covers every title you ship

Perpetual — yours to keep

For independent developers turning creations into products.

Everything in Personal, plus the right - and the tooling - to put your creation in customers' hands as a product of its own: package your Lua as license-bound bytecode, ship it with a freely redistributable runtime, and for multiplayer titles run the meeting point your players connect through. No accounts to operate, and none of our servers in the path.

What you get

  • Everything in Personal
  • Redistributable runtime to ship beside your creation
  • Sealed bytecode packaging - ship without shipping your source
  • Meeting point service for networked titles
  • Your players need no DatumHue purchase or account

Platform

Free in your browser.

Free browser · $39 desktop · $19/mo publish

For everyone - and for creators who want an audience.

The browser workstation is free for everyone, no license needed - open it and run everything the platform's creators publish. Creating your own happens on the platform desktop workstation - a perpetual license that always runs the latest release. Publish as a creator and the subscription connects your own package registry to the platform, putting everything you publish in front of every user for as long as you're subscribed.

What you get

  • Run everything free in the browser - no license needed
  • $39 desktop workstation, always the latest release
  • $19/mo creator registry, seen by every platform user
  • Cancel publishing anytime; the desktop license stays yours

Enterprise

The whole platform, on your infrastructure.

From $299/mo

Yearly billing - two months free

For organizations running the networked platform themselves.

Deploy the platform on infrastructure you control - self-hosted or fully air-gapped - and connect it to what you already run: file shares your people author against from any workstation, databases and data files queried live with SQL, curated web services and event feeds, and sign-in through your own identity provider. Licensed by concurrent seats, sized to your deployment.

What you get

  • The complete networked platform, self-hosted or air-gapped
  • File shares and databases as live, SQL-queryable mounts
  • Single sign-on through your identity provider
  • An internal app registry for what your teams publish
  • Concurrent-seat licensing; resize any time

Side by side

Edition comparison
PersonalIndiePlatformEnterprise
Price$79 one-time$199 one-timeFree · $39 · $19/moFrom $299/mo
LicensePerpetualPerpetualFree browser; perpetual desktopMonthly or yearly subscription
Updates5-year window5-year windowAlways the latestLatest while subscribed
Try before you buy14-day free trialFree in the browserStart monthly, cancel anytime
Works offlineFullyFullyNeeds the platformSelf-hosted or air-gapped
The whole creative APIYesYesYesYes
Commercial use of your workYesYesYesYes
Ship self-contained productsYes
Networked creationsMeeting point you runThrough the platformOn your infrastructure
Publish to the platform store$19/mo
Org files, databases, and sign-on as mountsYes
Self-host the platformYes

Common questions

What does "perpetual" mean here?

Your license never expires. Every release published inside your update window is yours to run forever, offline, with no further payment. What ends after five years is the entitlement to newer releases - never your access to what you already have.

What happens when my update window ends?

Nothing changes for anything you already run: releases from inside your window keep working forever. To run releases published after it, purchase a new window whenever you choose - each runs five years from its own purchase date, and your license identity carries over, so installs, shipped creations, and their data are untouched.

How does the free trial work?

Enter your email and we send you a license that unlocks the full Personal workstation for 14 days - no card, no account. When the trial ends your work stays where it is, and a purchased license picks up exactly where you left off. One trial per email address.

Personal or the Platform desktop - which one do I want?

Personal is fully offline: no platform connection, a five-year update window, and yours even if you never connect to anything. The Platform desktop is the connected counterpart: cheaper, always the latest release, with its content coming from the platform - so it needs the platform to be reachable. If you want the workstation that is entirely yours, pick Personal; if you live in the platform's package ecosystem, pick the Platform desktop.

Can a team share one Personal license?

No - one purchase covers one person, on any number of their own machines. A team or organization may have at most five contributors using DatumHue under individual licenses; past that, the enterprise agreement is the right home. The exact terms are in the license texts on the licenses page.

What do my players need if I ship a game with Indie?

Nothing from us. Your creation ships with a freely redistributable runtime bound to your license, and your players run it like any other program - no DatumHue purchase, no account, no connection to our infrastructure.

Where do I download it, and where do I report problems?

Desktop downloads are published on our GitHub releases page (github.com/datumhue/datumhue) - the launcher then keeps your install current with signed, verified updates. Problems and ideas go to the issue tracker on the same repository; both are linked in the footer.

Are licenses refundable?

A license is delivered the moment you check out, and at checkout you request immediate delivery and waive the distance-selling withdrawal period - so a delivered license is not refundable as a change of mind. Statutory remedies for a defective product are unaffected. The free trial exists so you can be sure before you buy.