Use this track when the canvas is primarily a whiteboard, sketch surface, annotation tool, diagramming app, review board, or drawing-heavy editor.
The shorter Add Drawing Tools guide shows how drawing support fits into a
canvas. This track goes deeper: toolbars, cursor modes, browser drafts,
committed vector items, vector item rendering, point editing, erasing, drawing-owned content
elements, persistence, shared canvases, import/export, and recipes.
What LivePanels Gives A Drawing App
LivePanels gives drawing apps a server-authoritative canvas model:
- in-progress vector items are browser-owned drafts;
- completed drafts become drawing commands;
- accepted drawing commands create canvas items;
- committed vector items render in the world vector layer;
- drawing elements use the same item placement model;
- selection, history, clipboard, persistence, and shared mode keep working because drawings become canvas state.
Your application still owns the product:
- the toolbar and palette;
- which tools are available;
- selected-vector-item inspectors;
- import/export screens;
- annotation meaning;
- document, asset, or domain records associated with drawings;
- any custom SVG or HTML presentation around vector items and elements.
The Drawing App Loop
A drawing app usually follows this loop:
use the drawing toolkit
-> render a canvas runtime
-> render application toolbar controls
-> set browser cursor mode and draft style
-> let the browser own the active draft
-> commit the finished draft as a drawing command
-> render committed vector items from canvas state
-> edit, erase, persist, copy, and share committed vector items as itemsThe important part is that drawing does not become a separate client-side document store. The drawing surface is a capability on the canvas.
Minimum Drawing Canvas
Start with the drawing toolkit:
defmodule MyAppWeb.SketchLive do
use MyAppWeb, :live_view
use LivePanels.Canvas,
pubsub: MyApp.PubSub,
domains: [:drawing]
use LivePanels.Toolkit.Drawing
@default_style %{
color: "#111827",
width: 3,
fill_color: "none",
stroke_style: "solid",
opacity: 100,
roundness: 0
}
@impl true
def mount(_params, _session, socket) do
{:ok,
socket
|> assign(:cursor_mode, :drag)
|> assign(:draw_tool, :pen)
|> assign(:draw_style, @default_style)}
end
@impl true
def render(assigns) do
~H"""
<main class="sketch">
<nav class="sketch-toolbar">
<button phx-click={set_cursor_mode_js(@livepanels.canvas_id, :drag, push: "set_cursor_mode")}>
Select
</button>
<button
phx-click={
set_cursor_mode_js(@livepanels.canvas_id, :draw,
tool: :pen,
style: @draw_style,
push: "set_cursor_mode"
)
}
>
Pen
</button>
<button
phx-click={
set_cursor_mode_js(@livepanels.canvas_id, :draw,
tool: :rectangle,
style: @draw_style,
push: "set_cursor_mode"
)
}
>
Rectangle
</button>
<button phx-click={set_cursor_mode_js(@livepanels.canvas_id, :erase, push: "set_cursor_mode")}>
Erase
</button>
</nav>
<.canvas_runtime {canvas_runtime_attrs(assigns, cursor_mode: @cursor_mode)} />
</main>
"""
end
@impl true
def handle_event("set_cursor_mode", %{"mode" => mode} = params, socket) do
cursor_mode =
case mode do
"draw" -> :draw
"erase" -> :erase
"marquee" -> :marquee
_ -> :drag
end
draw_tool =
case Map.get(params, "tool") do
"pen" -> :pen
"freehand" -> :pen
"rectangle" -> :rectangle
"arrow" -> :arrow
"line" -> :line
"polyline" -> :polyline
"ellipse" -> :ellipse
"diamond" -> :diamond
"triangle" -> :triangle
"text" -> :text
_ -> socket.assigns.draw_tool
end
{:noreply,
socket
|> assign(:cursor_mode, cursor_mode)
|> assign(:draw_tool, draw_tool)}
end
endThe runtime renders committed vector items automatically. The toolbar tells the browser what to do next.
Track Map
Read the guides in this order when building a drawing app:
Tools, Cursor Modes, And Draftsfor the browser interaction model.Commit Vector Items To Canvas Statefor vector item creation and server commands.Render And Style Vector Itemsfor the vector layer and application presentation.Edit Points, Labels, Text, And Arrowheadsfor vector item inspectors and geometry editing.Erase, Select, Move, And Resizefor item behavior around committed vector items.Images, Frames, Embeds, And Drawing Elementsfor drawing-owned content.Persist, Share, Import, And Exportfor recovery and collaboration.Drawing App Recipesfor common application patterns.
Drawing And Other Capabilities
Drawing can coexist with ordinary item content, graph connectors, shared mode, and custom application UI because committed drawings are canvas items.
For example, a review app can use vector items for annotations, ordinary content items for comments, shared mode for collaboration, and graph connectors for relationships between frames. LivePanels does not need a separate runtime for each capability. The application decides which capabilities are useful for the product.
What To Keep Out Of The Core Runtime
Do not put product meaning into the drawing runtime.
A rectangle might mean a highlight, a frame, a bounding box, a selected region, or a step in a diagram. LivePanels only needs to know that a committed vector item has kind, placement, points, style, and vector state.
Keep domain records in your app. Use LivePanels to make the drawing behavior consistent.