Fundamentals: Shapes & Annotations
Mix.install([
{:plotly_ex, "~> 0.1"},
{:kino, "~> 0.18"}
])
Fundamentals: Shapes & Annotations > Tickmode - Linear (Date)
For date axes, dtick is specified in milliseconds. 30 days =
30 * 24 * 60 * 60 * 1000 = 2_592_000_000. tick0 is the first tick as
an ISO date string.
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(
Scatter.new(
x: ["2020-01-01", "2020-02-01", "2020-03-01", "2020-04-01",
"2020-05-01", "2020-06-01", "2020-07-01"],
y: [1, 3, 2, 4, 3, 5, 4],
mode: "lines+markers"
)
)
|> Figure.update_layout(
title: "Linear Tickmode - Date Axis (30-day ticks)",
xaxis: %{
tickmode: "linear",
tick0: "2020-01-01",
dtick: 2_592_000_000
}
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Tickmode - Array
Set tickmode: "array" and provide explicit tick positions via tickvals
and labels via ticktext. Only the specified ticks are shown.
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(Scatter.new(y: [1, 2, 3, 4, 5], mode: "markers"))
|> Figure.update_layout(
title: "Array Tickmode",
xaxis: %{
tickmode: "array",
tickvals: [0, 1, 2, 3, 4],
ticktext: ["Apples", "Bananas", "Coconuts", "Dates", "Elderberries"]
}
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Using Tickformat
tickformat uses D3 number or time format strings. Use "%" to display
values as percentages (a value of 0.5 renders as “50%”).
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(
Scatter.new(
x: Enum.to_list(1..10),
y: [0.1, 0.24, 0.18, 0.35, 0.29, 0.41, 0.38, 0.52, 0.47, 0.6],
mode: "lines+markers"
)
)
|> Figure.update_layout(
title: "Y-Axis as Percentage",
yaxis: %{tickformat: "%"}
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Using Tickformat (Date)
For date axes, tickformat uses d3-time-format specifiers. Common tokens:
%d day, %B full month name, %b abbreviated month, %a abbreviated
weekday, %Y 4-digit year. Use \n for multi-line tick labels.
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(
Scatter.new(
x: ["2020-01-15", "2020-02-15", "2020-03-15",
"2020-04-15", "2020-05-15", "2020-06-15"],
y: [10, 14, 12, 18, 15, 20],
mode: "lines+markers"
)
)
|> Figure.update_layout(
title: "Custom Date Tick Format",
xaxis: %{tickformat: "%d %B (%a)\n%Y"}
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Tickformatstops to Customize for Different Zoom Levels
tickformatstops is a list of maps, each with a dtickrange (two-element
list of tick-step thresholds) and a value (the format string applied at
that zoom level). Use nil as an open bound. This lets the tick format
adapt as the user zooms in or out.
alias Plotly.{Figure, Scatter}
# Generate hourly data for a few days
start_ms = ~N[2020-01-01 00:00:00] |> DateTime.from_naive!("Etc/UTC") |> DateTime.to_unix(:millisecond)
x = for h <- 0..72, do: start_ms + h * 3_600_000
y = Enum.map(0..72, fn h -> :math.sin(h / 6.0) + :rand.uniform() * 0.2 end)
Figure.new()
|> Figure.add_trace(Scatter.new(x: x, y: y, mode: "lines"))
|> Figure.update_layout(
title: "Tickformatstops — zoom in/out to see format change",
xaxis: %{
type: "date",
tickformatstops: [
%{dtickrange: [nil, 1000], value: "%H:%M:%S.%L ms"},
%{dtickrange: [1000, 60_000], value: "%H:%M:%S s"},
%{dtickrange: [60_000, 3_600_000], value: "%H:%M m"},
%{dtickrange: [3_600_000, 86_400_000], value: "%H:%M h"},
%{dtickrange: [86_400_000, 604_800_000], value: "%e. %b d"},
%{dtickrange: [604_800_000, "M1"], value: "%e. %b w"},
%{dtickrange: ["M1", "M12"], value: "%b '%y M"},
%{dtickrange: ["M12", nil], value: "%Y Y"}
]
}
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Using Exponentformat
Use exponentformat to control how large numbers are displayed on axes.
Set showexponent: "all" to apply formatting to all ticks. Options for
exponentformat: "none", "e", "E", "power", "SI", "B".
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(
Scatter.new(
x: [1, 10, 100, 1_000, 10_000, 100_000, 1_000_000],
y: [1, 10, 100, 1_000, 10_000, 100_000, 1_000_000],
mode: "markers"
)
)
|> Figure.update_layout(
title: "Exponent Format on Axes",
xaxis: %{showexponent: "all", exponentformat: "e"},
yaxis: %{showexponent: "all", exponentformat: "power"}
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Include Locale Config
Set config.locale to a BCP 47 language tag (e.g. "fr", "de", "es")
to apply locale-specific number and date formatting. The locale file is
loaded dynamically from the Plotly CDN (this is handled automatically by
the PlotlyLive widget).
Combined with tickformat, this formats date labels in the selected locale’s
language (e.g. French month names).
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(
Scatter.new(
x: ["2020-01-01", "2020-02-01", "2020-03-01",
"2020-04-01", "2020-05-01", "2020-06-01"],
y: [10, 14, 12, 18, 16, 20],
mode: "lines+markers"
)
)
|> Figure.update_layout(
title: "Locale Config — French Date Formatting",
xaxis: %{tickformat: "%d %B %Y"}
)
|> Plotly.show(config: %{locale: "fr"})
Note: locale support requires an internet connection in Livebook to load the locale file from the Plotly CDN. See notebook 13 (Change the Default Locale) for more locale options.
Fundamentals: Shapes & Annotations > Highlighting Time Series Regions with Rectangle Shapes
Add layout.shapes — a list of shape maps — to overlay geometric shapes on
a chart. Set xref: "x" to anchor the x bounds to data coordinates and
yref: "paper" to span the full height (0 = bottom, 1 = top) regardless of
y scale. Use line: %{width: 0} to suppress the border.
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(
Scatter.new(
x: ["2015-02-01", "2015-02-02", "2015-02-03", "2015-02-04",
"2015-02-05", "2015-02-06", "2015-02-07", "2015-02-08",
"2015-02-09", "2015-02-10"],
y: [1, 3, 2, 4, 5, 3, 4, 6, 5, 7],
mode: "lines"
)
)
|> Figure.update_layout(
title: "Highlighted Time Regions",
shapes: [
%{
type: "rect",
xref: "x",
yref: "paper",
x0: "2015-02-04",
y0: 0,
x1: "2015-02-06",
y1: 1,
fillcolor: "#d3d3d3",
opacity: 0.5,
line: %{width: 0}
},
%{
type: "rect",
xref: "x",
yref: "paper",
x0: "2015-02-08",
y0: 0,
x1: "2015-02-10",
y1: 1,
fillcolor: "#d3d3d3",
opacity: 0.5,
line: %{width: 0}
}
]
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Highlighting Clusters of Scatter Points with Circle Shapes
Use type: "circle" shapes with xref: "x" and yref: "y" to draw
circles anchored to data coordinates, overlaying scatter point clusters.
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(
Scatter.new(
x: [0.5, 1.0, 1.5, 2.0, 2.5, 3.5, 4.0, 4.5],
y: [0.75, 0.5, 1.0, 0.75, 0.9, 3.5, 4.0, 3.75],
mode: "markers",
marker: %{size: 10}
)
)
|> Figure.update_layout(
title: "Circle Shapes Highlighting Clusters",
shapes: [
%{
type: "circle",
xref: "x",
yref: "y",
x0: 0.25,
y0: 0.25,
x1: 2.75,
y1: 1.25,
fillcolor: "rgba(50, 171, 96, 0.2)",
line: %{color: "rgba(50, 171, 96, 0.8)"}
},
%{
type: "circle",
xref: "x",
yref: "y",
x0: 3.25,
y0: 3.25,
x1: 4.75,
y1: 4.25,
fillcolor: "rgba(214, 39, 40, 0.2)",
line: %{color: "rgba(214, 39, 40, 0.8)"}
}
]
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Vertical and Horizontal Lines Positioned Relative to the Axes
Use type: "line" to draw straight lines. Set x0 == x1 for vertical
lines or y0 == y1 for horizontal lines. xref: "x" / yref: "y" anchors
to data coordinates. Use line.dash for dashed/dotted styles.
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(Scatter.new(x: [2, 4, 6], y: [4, 2, 5], mode: "markers"))
|> Figure.update_layout(
title: "Vertical and Horizontal Lines",
shapes: [
# Vertical line at x=2
%{
type: "line",
xref: "x",
yref: "paper",
x0: 2,
y0: 0,
x1: 2,
y1: 1,
line: %{color: "royalblue", width: 2, dash: "dot"}
},
# Horizontal line at y=3
%{
type: "line",
xref: "paper",
yref: "y",
x0: 0,
y0: 3,
x1: 1,
y1: 3,
line: %{color: "firebrick", width: 2, dash: "dashdot"}
}
]
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Circle
Draw circles in paper coordinates (0–1 on both axes, independent of data
scale) using xref: "paper" and yref: "paper". Also demonstrates an
unfilled circle (no fillcolor) versus a filled one.
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(Scatter.new(x: [1.5, 4.5], y: [0.75, 0.75], mode: "text", text: ["Unfilled", "Filled"]))
|> Figure.update_layout(
title: "Circle Shapes",
xaxis: %{range: [0, 6]},
yaxis: %{range: [0, 1.5]},
shapes: [
%{
type: "circle",
xref: "x",
yref: "y",
x0: 1,
y0: 0.25,
x1: 2,
y1: 1.25,
line: %{color: "RoyalBlue"}
},
%{
type: "circle",
xref: "x",
yref: "y",
x0: 3,
y0: 0.25,
x1: 5,
y1: 1.25,
line: %{color: "LightSeaGreen"},
fillcolor: "PaleTurquoise"
}
]
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Rectangle Positioned Relative to the Plot and to the Axes
You can mix reference frames: xref: "paper" places x bounds in paper
coordinates (0–1), while yref: "y" anchors y to data coordinates. This
lets you draw a shape that always spans the full width but sits at a
specific y range.
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(Scatter.new(x: [1, 2, 3, 4, 5], y: [1, 3, 2, 4, 3], mode: "lines"))
|> Figure.update_layout(
title: "Rectangle: Paper x-ref, Axis y-ref",
shapes: [
%{
type: "rect",
xref: "paper",
yref: "y",
x0: 0,
y0: 2,
x1: 1,
y1: 3,
fillcolor: "LightSalmon",
opacity: 0.4,
line: %{width: 0}
}
]
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Rectangle Positioned Relative to the Axes
Both xref: "x" and yref: "y" anchor the rectangle to data coordinates.
The rectangle resizes with zoom.
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(Scatter.new(x: [1.5, 2.5], y: [1.5, 2.5], mode: "markers", marker: %{size: 10}))
|> Figure.update_layout(
title: "Rectangle Relative to Axes",
xaxis: %{range: [0, 4]},
yaxis: %{range: [0, 4]},
shapes: [
%{
type: "rect",
xref: "x",
yref: "y",
x0: 1,
y0: 1,
x1: 3,
y1: 3,
fillcolor: "LightGreen",
opacity: 0.4,
line: %{color: "DarkGreen", width: 2}
}
]
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Lines Positioned Relative to the Plot and to the Axis
Mix xref: "paper" (plot-relative, 0–1) with yref: "y" (data-relative)
to draw a line that always spans the full width at a fixed data y-value.
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(Scatter.new(x: [0, 1, 2, 3, 4, 5], y: [1.5, 2, 3, 2.5, 4, 3], mode: "lines"))
|> Figure.update_layout(
title: "Lines: Mixed Paper/Axis Reference",
shapes: [
%{
type: "line",
xref: "paper",
yref: "y",
x0: 0,
y0: 2.5,
x1: 1,
y1: 2.5,
line: %{color: "Crimson", width: 2, dash: "dot"}
}
],
annotations: [
%{
text: "Threshold",
xref: "paper",
yref: "y",
x: 0.05,
y: 2.6,
showarrow: false,
font: %{color: "Crimson"}
}
]
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Basic Arbitrary SVG Paths
Use type: "path" with a path string containing SVG path commands:
-
M x,y— move to (start point) -
L x,y— line to -
Q cx,cy x,y— quadratic Bézier -
C cx1,cy1 cx2,cy2 x,y— cubic Bézier -
Z— close path
All coordinates use xref/yref as with other shapes.
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(Scatter.new(x: [1.5], y: [1.5], mode: "markers", marker: %{size: 10}))
|> Figure.update_layout(
title: "SVG Path Shapes",
xaxis: %{range: [0, 3]},
yaxis: %{range: [0, 3]},
shapes: [
# Triangle via SVG path
%{
type: "path",
path: "M 1 1 L 2 1 L 1.5 2 Z",
fillcolor: "rgba(44, 160, 101, 0.5)",
line: %{color: "rgb(44, 160, 101)"}
}
]
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Venn Diagram with Circle Shapes
Overlapping circle shapes with transparency create a Venn diagram effect.
Use fillcolor with rgba for transparency and position circles using
paper coordinates.
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(
Scatter.new(
x: [1, 2, 1.5],
y: [1, 1, 1.75],
mode: "text",
text: ["A", "B", "A∩B"],
textfont: %{size: 18, color: "white"}
)
)
|> Figure.update_layout(
title: "Venn Diagram",
xaxis: %{range: [0, 3], showgrid: false, zeroline: false, showticklabels: false},
yaxis: %{range: [0, 3], showgrid: false, zeroline: false, showticklabels: false},
plot_bgcolor: "white",
shapes: [
%{
type: "circle",
xref: "x",
yref: "y",
x0: 0.2,
y0: 0.2,
x1: 2.2,
y1: 2.2,
fillcolor: "rgba(55, 128, 191, 0.6)",
line: %{color: "rgba(55, 128, 191, 1)"}
},
%{
type: "circle",
xref: "x",
yref: "y",
x0: 0.8,
y0: 0.2,
x1: 2.8,
y1: 2.2,
fillcolor: "rgba(255, 127, 14, 0.6)",
line: %{color: "rgba(255, 127, 14, 1)"}
}
]
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Creating Tangent Lines with Shapes
Compute tangent lines analytically and draw them as type: "line" shapes.
This example uses sin(x) and its derivative cos(x) to place tangent lines at
specific points.
alias Plotly.{Figure, Scatter}
x_curve = for i <- 0..100, do: i * :math.pi() / 50
y_curve = Enum.map(x_curve, &:math.sin/1)
# Tangent at x0: y = sin(x0) + cos(x0) * (x - x0)
tangent_at = fn x0 ->
y0 = :math.sin(x0)
slope = :math.cos(x0)
x1 = x0 - 0.6
x2 = x0 + 0.6
{x1, y0 + slope * (x1 - x0), x2, y0 + slope * (x2 - x0)}
end
{tx1_a, ty1_a, tx2_a, ty2_a} = tangent_at.(:math.pi() / 4)
{tx1_b, ty1_b, tx2_b, ty2_b} = tangent_at.(:math.pi())
Figure.new()
|> Figure.add_trace(Scatter.new(x: x_curve, y: y_curve, mode: "lines", name: "sin(x)"))
|> Figure.update_layout(
title: "Tangent Lines on sin(x)",
shapes: [
%{
type: "line",
xref: "x", yref: "y",
x0: tx1_a, y0: ty1_a, x1: tx2_a, y1: ty2_a,
line: %{color: "red", width: 2}
},
%{
type: "line",
xref: "x", yref: "y",
x0: tx1_b, y0: ty1_b, x1: tx2_b, y1: ty2_b,
line: %{color: "green", width: 2}
}
]
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Adding Labels to Shapes
Add a label map to any shape to annotate it directly. Key fields:
text, font (map with size, color), textposition (e.g.
"middle center", "top left"). The label moves with the shape.
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(Scatter.new(x: [1, 2, 3, 4, 5], y: [2, 4, 3, 5, 4], mode: "lines"))
|> Figure.update_layout(
title: "Shapes with Labels",
shapes: [
%{
type: "rect",
xref: "x",
yref: "y",
x0: 1.5,
y0: 2.5,
x1: 2.5,
y1: 4.5,
fillcolor: "rgba(255, 165, 0, 0.3)",
line: %{color: "orange"},
label: %{
text: "Region A",
font: %{size: 14, color: "darkorange"},
textposition: "middle center"
}
},
%{
type: "line",
xref: "x",
yref: "y",
x0: 3.5,
y0: 2,
x1: 3.5,
y1: 5,
line: %{color: "royalblue", width: 2, dash: "dot"},
label: %{
text: "Threshold",
font: %{size: 12, color: "royalblue"},
textposition: "end"
}
}
]
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Add Text Template in Pie Chart
texttemplate controls the text shown inside or outside each slice of a Pie
chart. Available tokens: %{label}, %{value}, %{percent} (formatted as
a percentage string, e.g. “25%”). Combine with textposition: "inside" to
display the labels within each slice.
alias Plotly.{Figure, Pie}
Figure.new()
|> Figure.add_trace(
Pie.new(
labels: ["Apples", "Bananas", "Cherries", "Dates"],
values: [40, 25, 20, 15],
texttemplate: "%{label}: %{value} (%{percent})",
textposition: "inside"
)
)
|> Figure.update_layout(title: "Pie Chart with Text Template")
|> Plotly.show()
Fundamentals: Shapes & Annotations > Customize Text Template
texttemplate works on ternary scatter plots too. Available tokens include
%{text} (from the text property), %{a}, %{b}, %{c} (ternary
coordinates). Use D3 format strings like :.2f for decimal precision.
Per-point textfont styling can be applied by passing lists for size and
color.
alias Plotly.{Figure, Scatterternary}
Figure.new()
|> Figure.add_trace(
Scatterternary.new(
a: [0.1, 0.2, 0.3, 0.4, 0.5],
b: [0.5, 0.4, 0.3, 0.2, 0.1],
c: [0.4, 0.4, 0.4, 0.4, 0.4],
mode: "markers+text",
text: ["Alpha", "Beta", "Gamma", "Delta", "Epsilon"],
texttemplate: "%{text}
(%{a:.2f}, %{b:.2f}, %{c:.2f})",
textposition: "bottom center",
textfont: %{
family: "sans-serif",
size: [14, 13, 12, 11, 10],
color: ["red", "blue", "green", "orange", "purple"]
},
marker: %{size: 12}
)
)
|> Figure.update_layout(title: "Customized Text Template on Ternary Plot")
|> Plotly.show()
Fundamentals: Shapes & Annotations > Set Date in Text Template
texttemplate on a Funnel chart with a date-typed y-axis uses %{label} to
render the date label text inside each funnel stage.
alias Plotly.{Figure, Funnel}
Figure.new()
|> Figure.add_trace(
Funnel.new(
x: [1200, 909, 600, 300, 80],
y: ["2024-01-15", "2024-02-15", "2024-03-15", "2024-04-15", "2024-05-15"],
texttemplate: "%{label}",
textposition: "inside"
)
)
|> Figure.update_layout(
title: "Funnel Chart with Date Labels via texttemplate",
yaxis: %{type: "date"}
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Adding Text to Data in Line and Scatter Plots
Set mode to include "text" (e.g. "markers+text" or "lines+markers+text")
and provide a text list to display labels directly on data points. Control
placement with textposition: "top center", "bottom center", "middle right", etc.
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(
Scatter.new(
x: [1, 2, 3, 4, 5],
y: [2, 1, 3, 1, 2],
mode: "markers+text",
text: ["Alpha", "Beta", "Gamma", "Delta", "Epsilon"],
textposition: "top center",
marker: %{size: 10},
name: "Team A"
)
)
|> Figure.add_trace(
Scatter.new(
x: [1, 2, 3, 4, 5],
y: [4, 3, 5, 3, 4],
mode: "lines+markers+text",
text: ["A", "B", "C", "D", "E"],
textposition: "bottom center",
name: "Team B"
)
)
|> Figure.update_layout(title: "Text Labels on Scatter/Line Points")
|> Plotly.show()
Fundamentals: Shapes & Annotations > Paper Referenced Annotations
Set xref: "paper" and yref: "paper" on an annotation to position it
relative to the plot area (0 = left/bottom, 1 = right/top), independent of
data coordinates. Use showarrow: false for text-only labels.
This is useful for adding titles, footnotes, or labels to specific corners of the chart.
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(Scatter.new(x: [1, 2, 3], y: [2, 4, 3], mode: "lines"))
|> Figure.update_layout(
title: "Paper-Referenced Annotations",
annotations: [
%{
text: "Top-left corner",
xref: "paper",
yref: "paper",
x: 0,
y: 1,
xanchor: "left",
yanchor: "top",
showarrow: false,
font: %{size: 14, color: "blue"}
},
%{
text: "Bottom-right corner",
xref: "paper",
yref: "paper",
x: 1,
y: 0,
xanchor: "right",
yanchor: "bottom",
showarrow: false,
font: %{size: 14, color: "red"}
},
%{
text: "Centre",
xref: "paper",
yref: "paper",
x: 0.5,
y: 0.5,
xanchor: "center",
yanchor: "middle",
showarrow: false,
font: %{size: 16, color: "green"}
}
]
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Simple Annotation
A basic annotation with an arrow pointing to a data coordinate. Key fields:
-
x,y: the point the arrow tip touches (in data coordinates) -
xref,yref:"x"/"y"to anchor to data -
ax,ay: arrow tail offset in pixels from the annotation text box -
arrowhead: integer 0–8 selecting the arrowhead style
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(
Scatter.new(
x: [0, 1, 2, 3, 4],
y: [0, 1, 4, 9, 16],
mode: "lines+markers"
)
)
|> Figure.update_layout(
title: "Simple Annotation",
annotations: [
%{
x: 2,
y: 4,
xref: "x",
yref: "y",
text: "Peak at (2, 4)",
showarrow: true,
arrowhead: 2,
ax: 0,
ay: -40
}
]
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Annotations with Log Axes
When an axis uses type: "log", annotation coordinates must be specified
in log₁₀ space. For a data value v, set the annotation coordinate to
:math.log10(v). For example, to annotate the point at y = 1000, set
y: :math.log10(1000) which equals 3.0.
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(
Scatter.new(
x: [1, 10, 100, 1_000, 10_000],
y: [1, 10, 100, 1_000, 10_000],
mode: "lines+markers"
)
)
|> Figure.update_layout(
title: "Annotations on Log Axes",
xaxis: %{type: "log"},
yaxis: %{type: "log"},
annotations: [
%{
x: :math.log10(100),
y: :math.log10(100),
xref: "x",
yref: "y",
text: "y = 100",
showarrow: true,
arrowhead: 2,
ax: 40,
ay: -40
},
%{
x: :math.log10(10_000),
y: :math.log10(10_000),
xref: "x",
yref: "y",
text: "y = 10,000",
showarrow: true,
arrowhead: 2,
ax: -60,
ay: 30
}
]
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Multiple Annotations
Pass a list of annotation maps to layout.annotations. Each map is
independent and can have different positions, styles, and arrow settings.
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(
Scatter.new(
x: ["2015-02-01", "2015-04-01", "2015-06-01", "2015-08-01",
"2015-10-01", "2015-12-01"],
y: [32, 55, 43, 91, 81, 53],
mode: "lines+markers"
)
)
|> Figure.update_layout(
title: "Multiple Annotations on a Time Series",
annotations: [
%{
x: "2015-04-01",
y: 55,
xref: "x",
yref: "y",
text: "Local max",
showarrow: true,
arrowhead: 3,
ax: 0,
ay: -40
},
%{
x: "2015-08-01",
y: 91,
xref: "x",
yref: "y",
text: "Global max",
showarrow: true,
arrowhead: 3,
ax: 0,
ay: -40,
font: %{color: "red", size: 14}
},
%{
x: "2015-02-01",
y: 32,
xref: "x",
yref: "y",
text: "Start",
showarrow: false,
xanchor: "left"
}
]
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Subplot Annotations
To annotate a specific subplot, set xref and yref to the subplot’s axis
names: "x2" / "y2" for the second subplot, "x3" / "y3" for the
third, etc. Use "paper" references for annotations that span the whole figure.
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(Scatter.new(x: [1, 2, 3], y: [4, 5, 6], mode: "lines", name: "Subplot 1"))
|> Figure.add_trace(
Scatter.new(
x: [20, 30, 40],
y: [50, 60, 70],
mode: "lines",
name: "Subplot 2",
xaxis: "x2",
yaxis: "y2"
)
)
|> Figure.update_layout(
title: "Subplot Annotations",
xaxis: %{domain: [0, 0.45]},
xaxis2: %{domain: [0.55, 1]},
yaxis2: %{anchor: "x2"},
annotations: [
%{
x: 2,
y: 5,
xref: "x",
yref: "y",
text: "Left subplot",
showarrow: true,
arrowhead: 2,
ax: 0,
ay: -30
},
%{
x: 30,
y: 60,
xref: "x2",
yref: "y2",
text: "Right subplot",
showarrow: true,
arrowhead: 2,
ax: 0,
ay: -30
}
]
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > 3D Annotations
Add annotations to 3D plots via layout.scene.annotations — a list of maps
with x, y, z (data coordinates), text, and optional arrow/font
settings. Set showarrow: false for floating text labels.
alias Plotly.{Figure, Scatter3d}
Figure.new()
|> Figure.add_trace(
Scatter3d.new(
x: [1, 2, 3, 4, 5],
y: [1, 3, 2, 4, 3],
z: [2, 4, 3, 5, 4],
mode: "markers+lines",
marker: %{size: 6}
)
)
|> Figure.update_layout(
title: "3D Annotations",
scene: %{
annotations: [
%{
x: 2,
y: 3,
z: 4,
text: "Peak Point",
showarrow: true,
arrowhead: 2,
font: %{size: 14, color: "red"}
},
%{
x: 4,
y: 4,
z: 5,
text: "Maximum",
showarrow: false,
font: %{size: 12, color: "blue"}
}
]
}
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Custom Text Color and Styling
Use textfont on a trace (when mode includes "text") to control the
font of data-point labels. Accepts family, size (scalar or list), and
color (scalar or list for per-point styling).
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(
Scatter.new(
x: [1, 2, 3, 4, 5],
y: [2, 1, 4, 3, 5],
mode: "markers+text",
text: ["Alpha", "Beta", "Gamma", "Delta", "Epsilon"],
textposition: "top center",
textfont: %{
family: "Courier New, monospace",
size: [16, 14, 18, 12, 20],
color: ["red", "blue", "green", "orange", "purple"]
},
marker: %{size: 10}
)
)
|> Figure.update_layout(title: "Custom Text Color and Styling")
|> Plotly.show()
Fundamentals: Shapes & Annotations > Styling and Coloring Annotations
Customize annotation boxes with bgcolor, bordercolor, borderwidth, and
opacity. Style the text with font. Control the arrow with arrowcolor,
arrowsize, and arrowwidth.
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(
Scatter.new(x: [1, 2, 3, 4, 5], y: [2, 4, 3, 5, 4], mode: "lines+markers")
)
|> Figure.update_layout(
title: "Styled Annotations",
annotations: [
%{
x: 2,
y: 4,
xref: "x",
yref: "y",
text: "Styled box",
showarrow: true,
arrowhead: 7,
ax: 0,
ay: -60,
bordercolor: "#c7c7c7",
borderwidth: 2,
borderpad: 4,
bgcolor: "#ff7f0e",
opacity: 0.8,
font: %{color: "white", size: 14}
},
%{
x: 4,
y: 5,
xref: "x",
yref: "y",
text: "Arrow styled",
showarrow: true,
arrowhead: 2,
arrowsize: 1.5,
arrowwidth: 2,
arrowcolor: "royalblue",
ax: -50,
ay: -30,
font: %{color: "royalblue", size: 13}
}
]
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > WebGL Text and Annotations
Scattergl (WebGL-accelerated scatter) supports mode: "text" for
rendering text labels efficiently over large datasets. The API is identical
to Scatter — only the rendering engine differs. Use Scattergl when you
have thousands of labelled points.
Note: The JavaScript example also demonstrates a range slider to pan through data. Range sliders require JavaScript interactivity not available in Livebook; the static chart below shows the text rendering capability.
alias Plotly.{Figure, Scattergl}
# Generate a moderate dataset to demonstrate text rendering
x = Enum.to_list(1..50)
y = Enum.map(x, fn i -> :math.sin(i / 5.0) * 10 end)
labels = Enum.map(x, fn i -> "P#{i}" end)
Figure.new()
|> Figure.add_trace(
Scattergl.new(
x: x,
y: y,
mode: "markers+text",
text: labels,
textposition: "top center",
textfont: %{size: 9},
marker: %{size: 4}
)
)
|> Figure.update_layout(
title: "WebGL Text Rendering (Scattergl)",
xaxis: %{title: "Index"},
yaxis: %{title: "Value"}
)
|> Plotly.show()
Fundamentals: Shapes & Annotations > Tickmode - Linear
Set tickmode: "linear" on an axis to place ticks at a fixed interval
starting from tick0. The dtick value controls the step between ticks.
alias Plotly.{Figure, Scatter}
Figure.new()
|> Figure.add_trace(Scatter.new(y: [1, 2, 3, 4, 5], mode: "markers"))
|> Figure.update_layout(
title: "Linear Tickmode",
xaxis: %{tickmode: "linear", tick0: 0.5, dtick: 0.75}
)
|> Plotly.show()