Getting Started
Plottr is a strictly-typed, animated GUI graph rendering library for Roblox. It renders line and bar charts out of plain GUI instances — no UI framework dependencies — with animated entries, gradient infill, curve smoothing, tooltips, legends, and realtime streaming.
Installation
Wally
Add Plottr to your wally.toml:
[dependencies]
Plottr = "alternativelua/plottr@0.2.0"
Then run wally install.
Manual
Copy the src folder into your project (e.g. as ReplicatedStorage.Plottr)
using Rojo or by importing a model file.
Your first graph
local Plottr = require(ReplicatedStorage.Plottr)
-- Any GuiObject works as a container; the graph fills it.
local graph = Plottr.new(containerFrame, {
theme = Plottr.Themes.Midnight,
smoothing = { enabled = true },
xLabels = { "Jan", "Feb", "Mar", "Apr", "May", "Jun" },
})
graph:setSeries({
{ name = "Revenue", values = { 4200, 5100, 4800, 6900, 8400, 9600 } },
{ name = "Costs", values = { 3100, 3300, 3600, 4100, 4400, 4900 }, kind = "bar" },
})
The graph animates in, re-renders automatically when data or config changes, and redraws instantly when the container resizes.
Realtime data
Use Graph:push with the maxPoints window to stream
values without replaying the entry animation. Pin the axis with yMin/yMax
so it does not rescale under the incoming data:
local live = Plottr.new(containerFrame, {
maxPoints = 40,
showPoints = false,
yMin = 0,
yMax = 100,
})
live:setSeries({ { name = "CPU %", values = {} } })
task.spawn(function()
while task.wait(0.2) do
live:push("CPU %", getCpuSample())
end
end)
Each redraw recycles the instances the last one built, so streaming costs property writes rather than a rebuild of the chart.
Interaction
Points and bars show a tooltip on hover or tap. With more than one series the
legend appears, and clicking an entry hides or shows that series — pass
legendToggle = false to opt out. Clicks on the data itself are reported to
handlers registered with Graph:onPointClicked:
graph:onPointClicked(function(event)
print(`{event.seriesName} #{event.index} = {event.value}`)
end)
Configuration
Every option lives on Config, and both
Plottr.new and Graph:configure
take a partial that is merged over the current values — anything you leave out
keeps what it had. To clear an optional field back to nil, pass Plottr.None:
graph:configure({ maxPoints = Plottr.None, title = Plottr.None })
Wrong types, non-finite numbers, and negative sizes throw. Options that only
affect cost or appearance are clamped instead: yTickCount to 2–50,
maxXTickCount to 1–50, smoothing.amount to 0–1, smoothing.samples to
2–64, fill.resolution to at least 1, and the transparencies to 0–1.
Labels are yours to format. formatValue writes the y-axis ticks and
tooltips, and xLabels takes either an array or a function — use the function
form for streaming data, where array indices stop lining up once values scroll
off the front:
local graph = Plottr.new(containerFrame, {
title = "Server load",
xAxisTitle = "Sample",
yAxisTitle = "Percent",
formatValue = function(value)
return string.format("%d%%", value)
end,
xLabels = function(index)
return if index % 5 == 0 then `t-{index}` else nil
end,
})
Themes
Start from a preset and override what you need with
Themes.extend. font and textSize are optional and
default to BuilderSans Medium at 12px; palette must have at least one
colour.
local brand = Plottr.Themes.extend(Plottr.Themes.Dark, {
name = "Brand",
palette = { Color3.fromRGB(255, 115, 0) },
textSize = 14,
})
graph:setTheme(brand)
Dashboards
Plottr.newGroup lays out several graphs in a grid:
local group = Plottr.newGroup(containerFrame, { columns = 2, spacing = 12 })
local monthly = group:addGraph("Monthly")
local live = group:addGraph("Live", { maxPoints = 40 })
Next steps
- Browse the API reference — every option lives on
Config. - Theme it with the built-in presets or
Themes.extend.