Coaching Playbook Builder
What you'll learn
~40 min- Build a browser-based play diagramming tool with drag-and-drop player positioning
- Draw movement routes and passing lanes with SVG path rendering
- Organize plays into categories (offense, defense, set pieces, drills)
- Export play diagrams as PNG images and a printable playbook PDF
What you’re building
Nick coaches (or helps coach) his daughters’ youth teams. Before every practice he stands at a whiteboard, draws circles for players, arrows for movement, X’s for the other team, and a rough outline of the field. He snaps a photo with his phone. By the time practice starts at 5:30 PM, the photo is a washed-out blur of smeared marker lines, and the assistant coach is squinting at it saying “is that a pass or a run?” By the second week of the season, Nick has 40 whiteboard photos in his camera roll with no labels, no organization, and no way to find the corner kick play he drew three Saturdays ago.
Other coaches use apps like Tactic Board or CoachPaint. Those apps cost $10-15 per month, lock your plays behind a subscription, and half of them are designed for professional clubs — not a U10 futsal team running a 3-1 diamond rotation. Nick does not need video analysis or GPS tracking. He needs a clean field, draggable players, movement arrows, and a way to save and print his plays.
You are going to build a browser-based play diagramming tool that does exactly that. Pick a sport (soccer, futsal, or football). Drag player tokens onto an accurately marked field. Draw movement routes — runs, passes, dribbles, screens — as SVG paths with arrow heads. Save plays into a library organized by category. Chain plays into a practice plan with time blocks. Export any play as a PNG image or generate a full printable playbook PDF. One tool for the U8 soccer team’s 4-3-3 attack pattern, the U10 futsal squad’s kickoff play, and — when nostalgia hits — the punt return scheme from Nick’s JMU days.
Here is how play communication works in youth sports: the head coach draws something on a whiteboard, takes a blurry photo, texts it to the group chat, and the assistant coach replies “what’s the triangle near the goal?” A week later, nobody can find it. Or the coach just describes the play verbally at practice and hopes eight-year-olds remember the shape. A visual tool that produces clean, labeled, shareable diagrams is the difference between “I think we practiced this” and “here’s the play, page 3 of the playbook.” Kids learn visually. Clean diagrams stick. Whiteboard photos do not.
This is the same architectural pattern behind Figma, Miro, Canva, and every tactical board app on the market. An HTML5 Canvas renders the static background (the field). An SVG overlay handles interactive elements (player tokens, route paths) because SVG elements are DOM nodes you can attach event listeners to. A state manager tracks positions, routes, and metadata. An export pipeline converts the visual state to PNG (via canvas.toDataURL or html2canvas) and PDF (via Puppeteer). The core concepts — hit testing, drag state, coordinate transforms, SVG path rendering — are the same whether you are building a whiteboard app or a $50M design tool.
🔍Youth coaching primer: formations, set pieces, and why diagrams matter
If you have not coached youth sports, here is the context that makes this tool useful:
- Formations describe how players are positioned on the field at the start of play. In soccer, a 4-3-3 means 4 defenders, 3 midfielders, 3 forwards (plus the goalkeeper). In futsal (5v5 indoor), common formations are 3-1 (three across the back, one pivot up top), 2-2 (a diamond or square), and 1-2-1 (diamond). In football, formations like the I-formation, shotgun, and 4-3 defense describe player alignment. Formations are the starting picture — the “before” state of any play.
- Set pieces are plays that start from a dead ball — corner kicks, free kicks, throw-ins in soccer; kickoffs and penalty kicks in futsal; kickoffs, punt returns, and field goal attempts in football. Set pieces are the most coachable plays because you control the starting positions exactly. Youth teams that practice set pieces win games because most youth teams do not practice them at all.
- Routes and movement are the arrows on the diagram. A solid line might mean “run here.” A dashed line means “pass the ball here.” A wavy line means “dribble.” A thick line means “set a screen or block.” These conventions are not universal, but they are common enough that any coach recognizes them.
- Why diagrams matter for kids: children under 12 are overwhelmingly visual learners. Verbal instructions like “you go to the far post and she cuts inside” produce blank stares. A diagram with numbered players and colored arrows produces “oh, I go THERE.” The difference in comprehension is dramatic, and it translates directly to execution on the field.
- Drill sequencing: a practice plan is a sequence of activities — warmup (10 min), skill work like passing or dribbling (15 min), small-sided game like 3v3 (15 min), full scrimmage (15 min), cooldown (5 min). Each segment maps to specific plays or exercises. A drill sequencer chains these into a printable practice plan so every minute is accounted for and the assistant coaches know what comes next.
Who this is for
Nick played football at James Madison University — he was a punter, which means he spent four years studying special teams schemes, return formations, and directional kicking strategy. He has a kinesiology and sports management degree, so he thinks in terms of movement patterns, body mechanics, and training progressions. He is not a casual sports parent. He understands X’s and O’s natively.
Right now he is helping coach his daughters’ U10 futsal team and U8 soccer team. The futsal team runs a 3-1 diamond rotation and has two set pieces for kickoffs. The soccer team is learning a basic 4-3-3 shape and just started practicing corner kicks. Nick draws plays on a whiteboard at home, photographs them, and texts them to the other parents who help coach. The photos are bad. The organization is nonexistent. He wants a tool that makes him look as organized as he actually is — and more importantly, helps the kids learn the plays faster because the diagrams are clean, labeled, and consistent.
The showcase
The finished tool produces:
- Browser-based play editor with a configurable field — soccer (full 105x68m pitch with center circle, penalty areas, goal areas, corner arcs), futsal (40x20m court with center circle and penalty arcs), or football (100-yard field with end zones, yard lines, and hash marks). The field scales to fill the viewport.
- Drag-and-drop player tokens: colored circles with jersey numbers. Home team in one color, away team in another. Draggable to any position on the field. Right-click (or long-press on mobile) to edit the jersey number, player name, or position label (GK, CB, LW, etc.). Snap-to-grid option for clean alignment.
- Route drawing: click a player, click a destination point, and an SVG path appears between them with an arrow head. Line types: solid (run), dashed (pass), wavy (dribble), thick (screen/block). Curved routes by dragging the midpoint of any path. Route color matches the player’s team color. Ghost positions show the player’s starting location after drawing a route, so the diagram shows both “before” and “after.”
- Play library with categories: offense, defense, set pieces, transition, drills. Save plays with a name, category, sport, formation name, notes, and tags. Sidebar list grouped by category. Search by name or tag. Duplicate, rename, delete plays. Import and export the full library as JSON for backup or sharing with other coaches.
- Drill sequencer: drag plays from the library into a timeline. Set a duration for each segment (5 min, 10 min, 15 min). Add notes per segment (“focus on first touch,” “rotate goalkeepers every 3 minutes”). Total practice time counter updates as you add segments. Export the practice plan as a PDF with play diagrams embedded at each step.
- PNG export per play: click export, get a clean PNG image of the current diagram with title and formation name. Ready to text to the coaching group chat or print.
- Full playbook PDF: generate a multi-page PDF with one play per page — title, diagram, formation notes, and tags. Table of contents at the front organized by category. Hand this to the assistant coach on game day.
- Mobile-friendly touch support: drag players and draw routes with touch gestures so Nick can make adjustments on the sideline from his phone or tablet.
The prompt
Start your AI CLI tool in an empty directory and paste this prompt:
Build a Node.js + Express application called coaching-playbook that serves abrowser-based play diagramming tool for youth sports coaching. The tool letscoaches drag players onto a field, draw movement routes, organize plays intoa library, build practice plans, and export diagrams as PNG images and PDFplaybooks.
PROJECT STRUCTURE:coaching-playbook/ package.json server.js (Express server, routes, static file serving) public/ index.html (main editor page) library.html (play library browser) practice-plan.html (drill sequencer / practice plan builder) css/ style.css (all application styles) js/ field-renderer.js (draws the field on HTML5 Canvas) player-manager.js (drag-and-drop player tokens on SVG overlay) route-drawer.js (SVG path drawing for movement routes) play-library.js (save, load, organize, search plays) drill-sequencer.js (practice plan timeline builder) export-engine.js (PNG and PDF export via html2canvas) app.js (main application controller, wires everything) data/ plays/ (JSON storage, one file per play) practice-plans/ (JSON storage for practice plans) sample-plays.json (pre-loaded sample plays) views/ playbook-pdf.hbs (Handlebars template for full playbook PDF) practice-plan-pdf.hbs (Handlebars template for practice plan PDF)
REQUIREMENTS:
1. EXPRESS SERVER (server.js) - Runs on port 3000 (configurable via PORT env var) - Serves static files from public/ - API routes: GET /api/plays -> list all saved plays (summary: id, name, category, sport, updatedAt) GET /api/plays/:id -> get full play data (positions, routes, meta) POST /api/plays -> save a new play (generate UUID for id) PUT /api/plays/:id -> update existing play DELETE /api/plays/:id -> delete a play GET /api/plays/export -> export all plays as a single JSON file POST /api/plays/import -> import plays from a JSON file GET /api/practice-plans -> list all practice plans POST /api/practice-plans -> save a practice plan GET /api/practice-plans/:id -> get a practice plan POST /api/export/png -> receive play state JSON, return PNG image POST /api/export/playbook -> receive play IDs, generate multi-page PDF POST /api/export/practice -> receive plan ID, generate practice plan PDF - Store plays as individual JSON files in data/plays/ (filename: {id}.json) - On first start, if data/plays/ is empty, load sample-plays.json and save each play as a separate file - Use express-handlebars for PDF template rendering - Dependencies: express, express-handlebars, uuid, puppeteer, cors
2. FIELD RENDERER (public/js/field-renderer.js) Export a FieldRenderer class that draws sport-specific fields on an HTML5 Canvas element.
Constructor: new FieldRenderer(canvasElement, sport, options) - sport: "soccer", "futsal", or "football" - options: { gridOverlay: false, gridSpacing: 10, fieldColor: "#1a472a", lineColor: "#ffffff", lineWidth: 2 }
SOCCER FIELD (105m x 68m, scaled to canvas): - Outer boundary lines (touchlines and goal lines) - Center line and center circle (9.15m radius) - Center spot - Penalty areas: 40.3m x 16.5m from each goal line - Goal areas: 18.3m x 5.5m from each goal line - Penalty spots: 11m from goal line - Penalty arcs: arc of 9.15m radius from penalty spot, outside penalty area - Corner arcs: quarter circle of 1m radius at each corner - Goals: 7.32m wide, drawn as rectangles extending behind the goal line
FUTSAL COURT (40m x 20m, scaled to canvas): - Outer boundary lines - Center line and center circle (3m radius) - Center spot - Penalty areas: 6m radius quarter circles from each goal post - Penalty spots: 6m from goal line center - Second penalty spots: 10m from goal line center - Goals: 3m wide
FOOTBALL FIELD (100 yards + 10-yard end zones x 53.3 yards): - Outer boundary lines - End zones with diagonal hash pattern fill (slightly different shade) - Yard lines every 5 yards (solid white) - Yard numbers every 10 yards (10, 20, 30, 40, 50, 40, 30, 20, 10) positioned on both sides of the field - Hash marks: short tick marks at each yard on the sideline - Center hash marks at the NFL/college standard width
All fields: - Scale to fill the canvas while maintaining aspect ratio - Add 20px padding around the field - Optional grid overlay (light dashed lines at gridSpacing intervals) - Expose a getFieldBounds() method returning { x, y, width, height } of the playable area in canvas pixels (used for constraining player positions) - Expose a fieldToCanvas(fieldX, fieldY) and canvasToField(canvasX, canvasY) for coordinate conversion between field meters/yards and canvas pixels - Redraw method: render() clears and repaints the field
3. PLAYER MANAGER (public/js/player-manager.js) Export a PlayerManager class that handles player tokens as SVG elements overlaid on the canvas.
Constructor: new PlayerManager(svgElement, fieldRenderer) - svgElement: an SVG element positioned exactly over the canvas
Player token structure (SVG group): - Circle: 18px radius, fill color based on team - Text: jersey number centered in the circle, white, bold, 14px - Label: position abbreviation below the circle (GK, CB, LW, ST, etc.), 10px, team color - Ghost circle: semi-transparent version at the original position when a route has been drawn from this player (shows the "before" state)
Team colors: - Home: #0F766E (teal, matches course accent) - Away: #ef4444 (red) - Neutral/drill: #6b7280 (gray, for cone markers or reference points)
Features: - addPlayer(team, number, positionLabel, fieldX, fieldY) -> returns player id - removePlayer(id) - Drag and drop: mousedown/touchstart on a player token starts drag, mousemove/touchmove updates position, mouseup/touchend ends drag. Constrain to field bounds from fieldRenderer.getFieldBounds(). - Snap-to-grid: if enabled, snap to nearest grid intersection on drop - Right-click (contextmenu event) or long-press (500ms touch hold) on a player opens an edit popup: text inputs for jersey number, player name, and position label. Save button updates the token. Delete button removes it. - getPlayerPositions() -> returns array of { id, team, number, label, fieldX, fieldY } for serialization - setPlayerPositions(positions) -> restores players from saved data - Clear all: removeAllPlayers()
Toolbar integration (handled in app.js): - "Add Home Player" button: adds a home team player at field center with the next available jersey number - "Add Away Player" button: same for away team - "Add Marker" button: adds a neutral cone/marker token
4. ROUTE DRAWER (public/js/route-drawer.js) Export a RouteDrawer class that handles SVG path drawing for movement routes.
Constructor: new RouteDrawer(svgElement, playerManager)
Route drawing flow: 1. User clicks a player token (source) -- token highlights with a glow 2. User clicks a destination point on the field (or another player) 3. A route path is created between source and destination
Route types (selectable from toolbar): - "run": solid line, 3px stroke - "pass": dashed line (8px dash, 6px gap), 3px stroke - "dribble": wavy line (SVG path with sine-wave curves), 3px stroke - "screen": thick solid line, 6px stroke
Route rendering: - SVG <path> element with appropriate stroke-dasharray for line type - Arrow head at the endpoint: SVG <marker> element with a triangular arrowhead, size 10x7, filled with the route color - Wavy lines: generate a path string with cubic bezier curves that oscillate perpendicular to the line direction (amplitude 6px, wavelength 16px) - Route color: matches the source player's team color (teal for home, red for away) - Curved routes: each route has a draggable control point at the midpoint. Dragging this point converts the straight line into a quadratic bezier curve. The control point is a small circle (6px radius, white fill) visible on hover.
Features: - addRoute(sourcePlayerId, destX, destY, type) -> returns route id - removeRoute(id) - getRoutes() -> returns array of { id, sourceId, destX, destY, type, controlX, controlY } for serialization - setRoutes(routes) -> restores routes from saved data - Clear all: removeAllRoutes() - When a route is drawn FROM a player, create the ghost position on that player (via playerManager) showing their starting position
5. PLAY LIBRARY (public/js/play-library.js) Export a PlayLibrary class that handles saving, loading, and organizing plays.
Play data structure: { id: "uuid", name: "4-3-3 High Press Attack", category: "offense", // offense, defense, set-piece, transition, drill sport: "soccer", // soccer, futsal, football formation: "4-3-3", notes: "Left winger stays wide, right winger cuts inside...", tags: ["press", "attack", "wide-play"], players: [array from playerManager.getPlayerPositions()], routes: [array from routeDrawer.getRoutes()], fieldConfig: { sport: "soccer", gridOverlay: false }, createdAt: "ISO timestamp", updatedAt: "ISO timestamp" }
Features: - saveCurrent(name, category, sport, formation, notes, tags) -> POST to API - updateCurrent(id) -> PUT to API (saves current editor state to existing play) - loadPlay(id) -> GET from API, restore field, players, and routes in editor - listPlays() -> GET from API, return summary list - deletePlay(id) -> DELETE via API - duplicatePlay(id) -> load play, save as new with " (copy)" appended to name - searchPlays(query) -> filter plays by name or tag (client-side filtering) - exportAll() -> GET /api/plays/export, trigger download of JSON file - importPlays(file) -> POST /api/plays/import with JSON file contents
Sidebar UI (in index.html): - Collapsible sidebar on the right side of the editor (320px wide) - Grouped by category with collapsible headers: Offense (count), Defense (count), Set Pieces (count), Transition (count), Drills (count) - Each play entry shows: name, sport badge, formation, last updated - Click to load into editor - Hover shows action buttons: duplicate, rename, delete - Search input at the top of the sidebar - "Import" and "Export All" buttons at the bottom
6. DRILL SEQUENCER (public/js/drill-sequencer.js) Export a DrillSequencer class for building practice plans from plays.
Practice plan data structure: { id: "uuid", name: "Tuesday Practice - U10 Futsal", date: "2026-03-10", segments: [ { order: 1, playId: null, title: "Warmup - Dynamic Stretching", duration: 10, notes: "High knees, butt kicks, lateral shuffles" }, { order: 2, playId: "uuid-of-play", title: "3-1 Diamond Rotation", duration: 15, notes: "Focus on pivot movement and wall passes" }, { order: 3, playId: "uuid-of-play", title: "Kickoff Set Piece", duration: 10, notes: "Run it 5 times, rotate positions" }, { order: 4, playId: null, title: "3v3 Scrimmage", duration: 15, notes: "Apply rotation from drill 2" }, { order: 5, playId: null, title: "Cooldown and Review", duration: 5, notes: "Stretch, review key points, hand out playbook page" } ], totalDuration: 55, createdAt: "ISO timestamp" }
Features: - practice-plan.html page with a timeline view - Drag plays from a play list into the timeline - Add non-play segments (warmup, scrimmage, cooldown) via an "Add Segment" button with title and duration inputs - Reorder segments by dragging within the timeline - Set duration per segment (5, 10, 15, 20, 30 min presets or custom) - Add notes per segment - Total practice time counter at the top, updates as segments are added or removed - Remove segments with a delete button on each - Save practice plan to API - Load saved practice plans - Export as PDF: timeline layout with play diagrams embedded at each segment that references a play. Non-play segments show title, duration, and notes. Header shows plan name, date, total duration.
7. EXPORT ENGINE (public/js/export-engine.js) Export an ExportEngine class that handles PNG and PDF generation.
PNG export: - Capture the current editor state (canvas field + SVG overlay) as a single PNG image - Use html2canvas (loaded via CDN: https://html2canvas.hertzen.com/dist/ html2canvas.min.js) to capture the editor container element - Add a title bar at the top of the image: play name, formation, sport badge. White text on dark background (#111118). - Resolution: 2x for retina quality (set html2canvas scale: 2) - Trigger download as "{play-name}.png"
Playbook PDF export: - POST selected play IDs to /api/export/playbook - Server generates a multi-page PDF using Puppeteer: Page 1: Cover page -- "Coaching Playbook" title, coach name (from config), team name, date generated, play count Page 2: Table of contents -- plays grouped by category with page numbers Pages 3+: One play per page -- play name as header, field diagram (rendered server-side by loading the editor in headless Chrome with the play data), formation name, notes, tags - Puppeteer renders the playbook-pdf.hbs template with play data - Return PDF as download: "{team-name}-playbook.pdf"
Practice plan PDF export: - POST plan ID to /api/export/practice - Server generates a PDF with the timeline and embedded play diagrams - Header: plan name, date, total duration - Each segment: time block, title, duration, notes, and play diagram if the segment references a play - Return PDF as download: "practice-plan-{date}.pdf"
8. MAIN APPLICATION CONTROLLER (public/js/app.js) Wire everything together:
- Initialize FieldRenderer, PlayerManager, RouteDrawer, PlayLibrary, DrillSequencer, ExportEngine on page load - Toolbar at the top of the editor: Left section: Sport selector (soccer/futsal/football dropdown), Grid toggle checkbox Center section: Mode selector (select/move, add-home, add-away, add-marker, draw-route buttons). Draw-route shows route type sub-options (run, pass, dribble, screen) when active. Right section: Save button, Export PNG button, Export Playbook PDF button - Editor layout: Left: toolbar + canvas/SVG editor (fills remaining width) Right: play library sidebar (320px, collapsible) - Save dialog: when clicking Save, show a modal with inputs for play name, category dropdown, sport (pre-filled from current field), formation name, notes textarea, tags input (comma-separated). If editing an existing play, pre-fill all fields and show "Update" instead of "Save". - Keyboard shortcuts: Delete/Backspace: remove selected player or route Ctrl+S: save current play Ctrl+Z: undo last action (maintain an undo stack of player/route changes) Escape: cancel current drawing mode, deselect all G: toggle grid overlay 1/2/3/4: switch route type (run/pass/dribble/screen)
9. STYLING (public/css/style.css) Dark theme matching the course palette: - Page background: #09090b - Editor background: #111118 - Toolbar background: #141414, border-bottom: 1px solid #1e1e2a - Sidebar background: #0d0d12, border-left: 1px solid #1e1e2a - Text: #e5e5e5 (primary), #9ca3af (secondary) - Accent: #0F766E (teal) for active buttons, selected items, highlights - Button styles: #1e1e2a background, #2d2d3a hover, teal border when active - Player tokens: team colors with subtle drop shadow - Route paths: team color with 0.8 opacity - Save dialog modal: centered, #141414 background, subtle box-shadow - Field container: rounded corners (8px), subtle border (#1e1e2a) - Sidebar play entries: hover background #1a1a24, active (loaded) has teal left border - Sport badges: small colored pills (soccer: green, futsal: orange, football: brown) - Responsive: on screens < 768px, sidebar moves to bottom as a collapsible drawer. Toolbar wraps to two rows. Touch targets minimum 44px. - Print styles: white background for exported PDFs
10. SAMPLE DATA (data/sample-plays.json) Pre-load 6 sample plays so the tool is useful immediately:
a. Soccer - "4-3-3 High Press Attack" (category: offense) - 11 home players in a 4-3-3 formation - Routes: left winger runs wide, right winger cuts inside, striker makes a curved run behind the defense, central midfielder passes to the winger - Notes: "Left winger stays wide to stretch the defense. Right winger drifts central to create overload. Striker times the run to stay onside."
b. Soccer - "Corner Kick Near Post" (category: set-piece) - 11 home players + 4 away defenders positioned for a corner kick - Routes: one runner to near post, one to far post, one short option - Notes: "First runner near post drags a defender. Second runner attacks the space behind. Short option if defense is packed."
c. Futsal - "3-1 Diamond Rotation" (category: offense) - 4 outfield home players in a 3-1 diamond + goalkeeper - Routes: circular rotation pattern -- pivot drops, wings rotate up, fixo pushes forward - Notes: "Continuous rotation. Ball follows the movement. Pivot always receives to feet, never with back to goal."
d. Futsal - "Kickoff Play" (category: set-piece) - 4 outfield home players + goalkeeper at kickoff positions - Routes: quick pass back, wall pass through the middle, shot - Notes: "Must execute in under 4 seconds. Surprise element is the wall pass -- most teams expect the ball to go wide."
e. Football - "I-Formation Power Run" (category: offense) - 11 home players in I-formation (QB, FB, HB, 2 WR, TE, 5 OL) - Routes: pulling guard, fullback lead block, halfback follows through the hole - Notes: "Power run to the strong side. Guard pulls to the play-side. Fullback kicks out the end. Halfback reads the fullback's block and cuts upfield."
f. Football - "Punt Return Right" (category: set-piece) - 11 home players in punt return formation - Routes: return man catches, wall forms on the right sideline, two personal protectors peel back to lead block - Notes: "This one is personal. JMU punt return scheme, right return. The wall sets up at the 30 and the return man aims for the sideline. If the wall holds, it is a big play."
DEPENDENCIES: express, express-handlebars, uuid, puppeteer, cors
CDN LIBRARIES (loaded in HTML files, not npm):- html2canvas: https://html2canvas.hertzen.com/dist/html2canvas.min.js- Chart.js (optional, for practice plan time visualization): https://cdn.jsdelivr.net/npm/chart.jsThe prompt asks for PNG and PDF export via Puppeteer. Puppeteer downloads a Chromium binary (~300 MB) and can be finicky. If you want to start light, tell the LLM to skip the Puppeteer dependency and the PDF export routes. You can always add them later with a follow-up prompt. The editor, player management, route drawing, and play library work without Puppeteer. PNG export via html2canvas works client-side with no server dependency.
What you get
After your AI CLI tool finishes, set up the project:
cd coaching-playbooknpm installnode server.jsOpen http://localhost:3000 in your browser. You should see the play editor with a soccer field rendered on the canvas, a toolbar across the top, and a play library sidebar on the right.
First look checklist
- Field renders correctly: a green soccer pitch with accurate markings — center circle, penalty areas, goal areas, corner arcs. Switch to futsal in the dropdown and verify a smaller court appears with different markings. Switch to football and verify yard lines, end zones, and hash marks.
- Add players: click “Add Home Player” and a teal circle with a jersey number appears at center field. Drag it to a position. Add several more. Add away players (red circles). All tokens should drag smoothly within the field bounds.
- Draw routes: select the “Draw Route” mode and choose “pass” (dashed line). Click a home player, then click a point near another player. A dashed arrow should appear. Try “run” (solid), “dribble” (wavy), and “screen” (thick). Each should look visually distinct.
- Curved routes: hover over a route and a small white control point should appear at the midpoint. Drag it to curve the route into a bezier arc.
- Save and load: click Save, fill in a name and category, and save. The play should appear in the sidebar library. Click it to reload. All player positions and routes should restore exactly.
- Sample plays: the sidebar should show 6 pre-loaded plays. Click “4-3-3 High Press Attack” and verify 11 players appear in formation with routes drawn.
- PNG export: click “Export PNG.” A download should trigger with the play diagram as an image file.
Common issues and fixes
| Problem | Follow-up prompt |
|---|---|
| Canvas and SVG are misaligned | The player tokens on the SVG overlay don't line up with the field on the canvas. The SVG element needs to have the exact same dimensions and position as the canvas. Set both to the same width and height, position them both as absolute within a relative container div, and make sure neither has any padding or margin. |
| Players can be dragged outside the field | Players can be dragged off the field into the padding area. The drag handler needs to clamp the player position to the field bounds returned by fieldRenderer.getFieldBounds(). Use Math.max(bounds.x, Math.min(bounds.x + bounds.width, newX)) for the x coordinate and the same pattern for y. |
| Route arrow heads don’t appear | The route lines are drawn but there are no arrow heads at the end. SVG marker elements need to be defined in a <defs> section of the SVG element. Create a <marker> with id="arrowhead", viewBox="0 0 10 7", refX="10", refY="3.5", markerWidth="10", markerHeight="7", orient="auto". Inside it, put a <polygon points="0 0, 10 3.5, 0 7" />. Then add marker-end="url(#arrowhead)" to each route path element. |
| Wavy dribble lines look like straight lines | The dribble route type should render as a wavy line but it appears straight. The SVG path needs cubic bezier curves that oscillate. Generate the path by stepping along the line from start to end, and at each step, offset the control points perpendicular to the line direction by alternating positive and negative amounts (amplitude 6px, wavelength 16px). Build the path string with C commands, not L commands. |
| Touch drag doesn’t work on mobile | Dragging players works with mouse but not on touch devices. Add touchstart, touchmove, and touchend event listeners alongside the mouse events. Use e.touches[0].clientX and e.touches[0].clientY to get coordinates. Call e.preventDefault() on touchmove to prevent page scrolling while dragging. |
When Things Go Wrong
Use the Symptom → Evidence → Request pattern: describe what you see, paste the error, then ask for a fix.
How it works
The application uses a layered rendering architecture:
-
Canvas layer (
field-renderer.js) draws the static field background — grass, lines, markings, grid. It only redraws when the sport changes or the window resizes. Canvas is ideal for this because it renders as a flat bitmap: no DOM nodes, no event overhead, just pixels. Drawing a soccer field with all its arcs and lines is a sequence of Canvas API calls (arc,moveTo,lineTo,stroke,fill). -
SVG overlay (
player-manager.js,route-drawer.js) sits directly on top of the canvas and handles everything interactive. Player tokens are SVG<circle>and<text>groups. Routes are SVG<path>elements. Because SVG elements are part of the DOM, each one can have its own event listeners for drag, click, hover, and context menu. This is why the interactive elements use SVG instead of Canvas — Canvas would require manual hit testing (checking if a click’s coordinates fall within a drawn circle), while SVG handles that automatically. -
Play Library (
play-library.js) serializes the current editor state (player positions + route definitions + field config + metadata) as a JSON object and sends it to the Express server for persistence. Loading a play reverses the process: fetch the JSON, set the field sport, place the players, draw the routes. The library sidebar is standard DOM with event delegation for click handling. -
Drill Sequencer (
drill-sequencer.js) is a separate page that references saved plays by ID. It does not re-render plays — it stores references and fetches the diagram data at export time. The timeline is a vertical list of draggable segments with duration and notes fields. -
Export Engine (
export-engine.js) bridges the visual editor and the file system. For PNG, it uses html2canvas to composite the canvas and SVG layers into a single image. For PDF, it sends play data to the server where Puppeteer renders the Handlebars template in headless Chrome, re-creating each play’s diagram and capturing the result as a multi-page PDF.
Customize it
Add animated play replay
Add a "Replay" button to the editor toolbar. When clicked, it animates theplay step by step: players start at their original positions (ghost positions)and move along their routes to their destination positions over 2 seconds.Use requestAnimationFrame to animate the SVG player tokens along their routepaths. Show routes drawing progressively (the path stroke-dashoffset technique)so the lines appear to draw themselves as the players move. Add a speed control(0.5x, 1x, 2x) and a replay/pause button. This is like watching coach's filmbut for a diagram -- it shows the timing and sequence of movement.Add formation templates
Add a "Formations" dropdown to the toolbar that lists common formations forthe current sport. For soccer: 4-4-2, 4-3-3, 3-5-2, 4-2-3-1, 3-4-3. Forfutsal: 3-1, 2-2, 1-2-1, 4-0. For football: I-Formation, Shotgun, Spread,4-3 Defense, 3-4 Defense, Nickel. When a formation is selected, place thecorrect number of home team players at standard positions for that formation.If players already exist, ask to replace or merge. Store the formationdefinitions in a formations.json file.Add team roster integration
Add a "Roster" tab in the sidebar that lets the coach enter their team roster:player name, jersey number, preferred positions. Store the roster in aroster.json file via the API. When adding players to the field, show theroster as a dropdown so the coach picks a real player instead of a genericnumber. When a play is saved, the player tokens reference roster entriesby ID so if a name or number changes, all plays update automatically.Add practice plan sharing via email
Add a "Share" button on the practice plan page. When clicked, generate thepractice plan PDF and open a mailto: link with the PDF as context. The emailsubject should be "[Team Name] Practice Plan - [Date]" and the body shouldlist the segment titles and durations as plain text. For actual fileattachment, add a nodemailer integration with SMTP config from environmentvariables (SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS) and send the PDFas an attachment to a comma-separated list of email addresses.Try it yourself
- Generate the coaching playbook tool with the prompt above.
- Run
npm installand start the server withnode server.js. - Open
http://localhost:3000. Switch between soccer, futsal, and football fields. Verify the markings are correct for each sport. - Add 5 home players and 3 away players. Drag them into a formation. Right-click a player and change the jersey number and position label.
- Select “Draw Route” mode with “pass” type. Click a player, then click a destination. Draw 3-4 routes of different types (run, pass, dribble). Curve one route by dragging the midpoint.
- Save the play with a name, category, and formation. Verify it appears in the sidebar library. Click away, then click the saved play to reload it. Check that every player and route restores correctly.
- Export the play as a PNG. Open the image and verify it shows the complete diagram with title and formation name.
- Open the practice plan page. Add 4-5 segments mixing warmup blocks, saved plays, and a scrimmage. Set durations. Verify the total practice time counter updates correctly.
Key takeaways
- Canvas and SVG serve different roles in a visual editor. Canvas is fast for rendering static graphics like a field background. SVG is ideal for interactive elements because each element is a DOM node with its own event listeners. Layering them — canvas behind, SVG in front — gives you the best of both: fast rendering and easy interaction.
- Coordinate transformation is the core skill in any visual editor. Converting between field coordinates (meters or yards) and screen coordinates (pixels) is what makes dragging, drawing, and exporting work correctly. Every visual tool — from Figma to Google Maps — solves this same problem.
- Serialization turns visual state into portable data. Saving a play means converting SVG positions and paths into a JSON object. Loading means reversing the process. This serialize-deserialize pattern is how every design tool, game save system, and document editor works under the hood.
- The export pipeline bridges the screen and the real world. A PNG goes to the group chat. A PDF goes to the printer. The same data, rendered for different outputs. This is the same dual-format pattern from the pulse reporter — one source of truth, multiple presentations.
- Domain expertise is the differentiator, not code. The LLM wrote the Canvas API calls, the SVG event handlers, and the Express routes. Nick provided the knowledge that a 3-1 diamond rotation uses continuous movement, that corner kicks need a near-post runner, and that a punt return wall forms at the 30-yard line. The code is generic. The coaching knowledge is what makes the tool actually useful.
The coaching playbook tool uses an HTML5 Canvas for the field background and an SVG overlay for player tokens and routes. Why does the route drawer use SVG paths instead of drawing routes directly on the Canvas?
The complete Nick E. Playbook
Eight lessons. Eight tools. Here is everything you built:
| Lesson | Tool | What it does |
|---|---|---|
| 1 | Resident Response Studio | Converts rough notes into polished, brand-appropriate resident communications |
| 2 | Morning Maintenance Triage Board | Parses overnight work orders, prioritizes by urgency, generates a daily action queue |
| 3 | Renewal Risk & Outreach Planner | Scores renewal risk, segments residents into cohorts, drafts personalized outreach |
| 4 | Vendor & Make-Ready Orchestrator | Schedules unit turn workflows with dependency logic, vendor calendars, and delay alerts |
| 5 | Occupancy & Revenue Pulse Reporter | One-command weekly KPI report with trends, charts, and auto-generated narrative |
| 6 | The Daily Ops Command Center | Unifies all five property tools into a single daily operating hub with Morning Run automation |
| 7 | Kids Sports Schedule Coordinator | Multi-kid activity calendar with conflict detection, carpool planning, and weekly family view |
| 8 | Coaching Playbook Builder | Visual play diagramming tool with drag-and-drop formations, movement routes, and drill sequences |
Lessons 1 through 6 built the operational backbone of the Concord Crystal City. Resident communications that sound professional every time. Maintenance triage that prioritizes the right work orders. Renewal outreach that keeps occupancy stable. Vendor scheduling that gets units turned on time. Weekly reporting that replaces 90 minutes of manual Excel work. A command center that ties it all together with one click at 7 AM.
Lessons 7 and 8 took the same workflow — describe what you need, let the LLM build it, iterate — and applied it to Nick’s life outside the Concord. A family calendar that prevents scheduling collisions between two kids, three sports, and two parents. A coaching playbook that replaces whiteboard photos with clean, organized, exportable diagrams.
The point is not that Nick built eight specific tools. The point is that the skill transfers to any domain he understands. Property management, youth sports, family logistics, kinesiology research, JMU alumni events — the workflow is identical every time. Start with a problem. Describe it precisely. Let the AI write the code. Test it. Iterate with follow-up prompts. Deploy it.
Nick started this track with zero lines of code written in his life. He ends it with a full property operations toolkit, a family calendar, and a coaching playbook — all built by describing what he needed in plain English. The code is not the product. The product is the ability to see a problem, reach for a CLI tool, and have a working solution in 30 minutes. That ability does not expire. It does not require a subscription. It does not need a developer. It belongs to Nick.
The Concord runs a little smoother. Practice is a little more organized. The kids can actually read the plays. And the next time Nick sees a problem that looks like it needs software, he already knows what to do.