Color
Static colors, alpha, theme-aware palettes, and where dynamic per-bar coloring is and is not supported in DeltaDSL.
Every drawing primitive that paints pixels accepts a color argument. Colors are strings in any CSS-recognized format. There is no color type, no constants, no enums — the runtime parses the string when it forwards the draw record to the engine.
Color formats
DeltaDSL accepts every CSS color syntax:
| Format | Example |
|---|---|
| Hex shorthand | "#fff", "#0c0" |
| Hex 6-digit | "#0ECB81" |
| Hex 8-digit (with alpha) | "#0ECB8166" (40 % alpha) |
rgb() | "rgb(14, 203, 129)" |
rgba() | "rgba(14, 203, 129, 0.4)" |
hsl() | "hsl(150, 70%, 40%)" |
hsla() | "hsla(150, 70%, 40%, 0.4)" |
| Named | "red", "transparent", "steelblue" |
The 8-digit hex form (#RRGGBBAA) is the most compact way to specify both a hue and a transparency:
plotBand(upper, lower, color="#0ECB8124") // green, ~14 % alpha
For readability, rgba(...) is the recommended form when you want anyone reading the script to immediately see what the alpha is:
plotBand(upper, lower, color="rgba(14, 203, 129, 0.14)")
The project palette
The chart UI uses a fixed brand palette (Binance/Material). When you author a script that should "look native" on this terminal, prefer these hues:
| Role | Dark theme | Light theme |
|---|---|---|
| Bullish (BUY direction) | #0ECB81 | #26a69a |
| Bearish (SELL direction) | #F6465D | #ef5350 |
| Chrome accent (Binance amber) | #D4A60A | #A88408 |
| Neutral text | #c8c8d8 | #131722 |
| Pane background | #0F1215 | #ffffff |
Tuned alphas you'll want often:
| Use | Hex | rgba |
|---|---|---|
| Soft band fill (bullish) | #0ECB8124 | rgba(14,203,129,0.14) |
| Soft band fill (bearish) | #F6465D24 | rgba(246,70,93,0.14) |
| Bright band fill | #0ECB8166 | rgba(14,203,129,0.40) |
| Highlight tint (amber) | #F0B90B1F | rgba(240,185,11,0.12) |
| Horizontal grid line | rgba(150,150,150,0.18) | — |
The palette is documented in binance-amber-glass.mdc and lives at src/assets/styles/styles.scss (--mrd-amber-fg, --mrd-bull-fg, --mrd-red-fg). Scripts can't read those CSS variables (the WASM render pass needs literal RGB), so when a script needs the brand palette it inlines the hex.
Theme-aware coloring
Charts run in dark or light theme. Most scripts only need to look right on dark; if you want to support both, pick:
- Mid-saturation hues that read on both backgrounds — the project palette above is tuned for this.
- Use a stronger alpha on bands for light-theme backgrounds since the white background washes out 14 % alphas.
DeltaDSL does not expose a "is dark theme?" runtime flag today. If your script absolutely needs to switch palette by theme, expose it as an input:
@input theme = input.string("Dark", "Theme",
options=["Dark","Light"], group="Style")
bullColor = iff(theme == "Dark", "#0ECB81", "#26a69a")
bearColor = iff(theme == "Dark", "#F6465D", "#ef5350")
Note: iff over strings produces a string at script-eval time (each theme comparison resolves once before the bar loop runs), so this pattern is fine for plotLine's static-color requirement.
Static vs dynamic coloring (important)
The engine distinguishes between static colors (one color per output channel) and dynamic per-bar coloring (different colors on different bars).
Static color (default — most primitives)
Functions like plotLine, plotShape, plotBand, paneLine, paneBand, paneFill, etc. take ONE color per call. The color string is resolved once per evaluation, not per bar.
plotLine(close, color="#0ECB81", width=2) // one color
plotBand(upper, lower, color="rgba(14,203,129,0.14)") // one color
If you pass an iff(...) expression that returns different colors per bar (a "color series"), only the first bar's color is used for the whole line. This is intentional — drawing tens of thousands of variable-color line segments costs orders of magnitude more in the WASM pipeline than one solid line.
Dynamic per-bar coloring (the mask pattern)
To paint the same shape with different colors on different bars, split the data into multiple series — each masked to where its color applies — then call plotLine (or paneLine) once per color:
@pane "overlay"
ma = sma(close, 50)
isUp = close > ma
upPart = mask(close, isUp)
dnPart = mask(close, isUp == false)
plotLine(upPart, color="#0ECB81", width=2)
plotLine(dnPart, color="#F6465D", width=2)
mask(series, predicate) returns a copy of the series where the predicate is false replaced with na. The renderer breaks the polyline at na gaps, so the two segments don't visually connect. The result on the chart: a single line that switches color based on close > ma.
Use this pattern for:
- Trend ribbon (above MA = green, below = red).
- Volume bars colored by direction (
mask(volume, close > open)+mask(volume, close < open)). - Multi-zone oscillator (overbought / oversold / neutral).
For shape-style primitives (plotShape, plotShapeAt, plotMarker, plotChar), the engine accepts a per-bar color via the function's color argument when it's the COLOR for the marker on the bar where the trigger is true. Markers are point-events, not connected lines, so coloring per event is cheap.
Per-pane / per-shape primitives that DO accept dynamic color
These primitives evaluate their color argument on every emit, so passing different colors per bar (or per call) works as expected:
plotShape/plotShapeAt/plotMarker/plotChar— one shape per truthy bar; color resolves at emit.paneLabel/plotLabel/panePlotLabel— labels resolve color at draw call.bgcolor— background tint per bar.labelNew/boxNew/lineNew/polylineNew— persistent slots; thecoloryou pass on each frame replaces the slot's previous color (so animating slot colors works).drawingHline/drawingTrendline/drawingFibRet/drawingChannel— drawing-tool shapes; the color updates with every call.
Anywhere else (line / band / fill primitives that produce ONE polyline or area), assume static color.
Common patterns
Two-color trend line
ma = sma(close, 50)
isUp = close > ma
plotLine(mask(close, isUp), color="#0ECB81", width=2)
plotLine(mask(close, isUp == false), color="#F6465D", width=2)
Histogram with positive / negative tinting
@pane "below"
hist = macd_hist(close, 12, 26, 9)
posHist = mask(hist, hist >= 0)
negHist = mask(hist, hist < 0)
paneFill(posHist, base=0, color="rgba(14,203,129,0.5)")
paneFill(negHist, base=0, color="rgba(246,70,93,0.5)")
Three-zone oscillator (overbought / neutral / oversold)
@pane "below"
r = rsi(close, 14)
ob = mask(r, r >= 70)
os = mask(r, r <= 30)
mid = mask(r, r > 30 and r < 70)
paneRange(0, 100)
paneLine(mid, color="#F0B90B", width=1.5)
paneLine(ob, color="#F6465D", width=1.5)
paneLine(os, color="#0ECB81", width=1.5)
Background pulse on event
event = volume > sma(volume, 20) * 3
bgcolor(iff(event, "rgba(240,185,11,0.18)", "rgba(0,0,0,0)"))
bgcolor paints behind candles and supports dynamic per-bar colors, so an iff is fine here.
Persistent label that re-tints with state
ma = sma(close, 50)
labelNew("ma_state",
time[0], ma[0],
"MA50: " + tostring(ma[0], "0.00"),
color = iff(close[0] > ma[0], "#0ECB81", "#F6465D"),
anchor="left", size=12)
labelNew resolves color on every frame, so flipping by side works naturally.
Color helpers
Most scripts inline color literals. For more dynamic coloring, two helpers exist:
color.rgb(r, g, b, a=1.0)— build a CSS rgba string from numeric channels.color.new(base, alpha)— clone a base color with a different alpha (useful when you want one hue but different opacities per zone).
red = "#F6465D"
softRed = color.new(red, 0.14) // → "rgba(246,70,93,0.14)"
plotBand(upper, lower, color=softRed)
Anti-patterns
Don't pass an iff returning two colors to a static-color primitive
// ❌ This compiles, but the engine uses the FIRST bar's color for the
// entire line. Indistinguishable from a solid line.
plotLine(close, color=iff(close > ma, "#0ECB81", "#F6465D"))
// ✅ Use the mask pattern.
upPart = mask(close, close > ma)
dnPart = mask(close, close <= ma)
plotLine(upPart, color="#0ECB81", width=2)
plotLine(dnPart, color="#F6465D", width=2)
Don't gradient-fill bands with multiple plotBand calls
The engine does NOT support gradient fills today. Two stacked plotBand calls with different alphas produce two opaque bands, not a smooth gradient. If you need depth, use a single band with a tuned alpha (0.10..0.20 range usually).
Don't use the BUY/SELL palette for chrome
The chart's chrome (toolbar, settings, dialogs) uses Binance amber. The bullish / bearish hues are reserved for trade data. Scripts authored for this terminal should follow the same convention: amber for "informational" / neutral signals, green / red strictly for direction.
If your script paints a "bullish setup" annotation, green is correct. If it paints "value level" or "session vwap", amber is correct. If it paints a "high attention zone" without direction, neutral or amber.
Next
- Drawing reference — every primitive and which ones accept dynamic colors.
- Recipes — practical multi-color patterns.