Back to planner

Roadmap

The Locodex track planner is built in the open. Here is what's available today, what we're working on next, and where we'd like to take the project. Tap a chip below to filter by status.

Editor & Canvas

A precise, fast 2D editor for placing track pieces with millimetre-accurate geometry.

  • Pan, zoom, and fit to viewAvailable now

    Pan (middle / right drag, or the Pan tool) and scroll to zoom, with the 2D zoom bounded so it can't scroll off into empty space. A Fit-to-view button on the canvas frames the whole layout — centring and zooming to fit with a margin, or framing the baseboard when nothing's placed.

  • Middle-click drag to panAvailable now

    Middle-mouse drag pans the view in both 2D and 3D modes, alongside the existing right-click pan. The Pan tool in the left rail switches left-click to pan as well.

  • Snap-to-grid with toggleAvailable now
  • Infinite background gridAvailable now

    The 2D grid extends indefinitely as you pan instead of cutting off at an arbitrary tile boundary. Its cell / section spacing adapts to the zoom so the lines stay a legible size — the familiar 100 mm cell / 500 mm section at normal zoom, stepping down through 50, 20, 10, 5, 2 mm to a 1 mm graph-paper grid up close (and coarser zoomed out). Zoom is bounded to that range so the view can't scroll endlessly past where the grid stops adding detail.

  • Minimap overview of placementsAvailable now

    Bottom-right SVG minimap that traces every placement's centerline at a glance, with the currently selected piece highlighted in orange. Its scale is anchored to the layout (or baseboard footprint when defined), so panning the camera anywhere — including far off the layout — never rescales the map. The viewport overlay clips to the border, and a chevron points home when the camera has wandered fully outside the framed area.

    Read the docs
  • On-canvas scale barAvailable now

    A Google-Maps-style ruler in the bottom-left of the 2D canvas: a bar of a round real-world length (mm / cm / m) sized to the current zoom, so distances on the layout are readable at a glance without measuring. Replaces a non-functional placeholder zoom readout.

  • Standard 2D track symbologyAvailable now

    In the 2D (orthographic) view each placed piece is drawn with the standard model-railway plan symbology — a black outline tracing the track bed, plus a black cap line at every connector marking where pieces join, plus a marking for special pieces: a synthetic glyph for uncouplers (which import as a plain straight) and whatever marking a piece carries from the import (the buffer-stop drawing, edge marks). The colour-coded fill (piece kind, selection, off-board warning, layer tint) stays underneath. 3D keeps the plain extruded bed.

  • Footprint / schematic 2D toggleAvailable now

    A Footprint / Schematic switch in the canvas's top-left corner — like AutoCAD's in-viewport visual-style control — flips the 2D view between the footprint — the sleeper outline that matches the physical piece, the default — and a schematic: a thin centerline per piece for reading dense trackwork at a glance. Both are the same editing surface (identical snapping, selection and connection); only the drawing changes. The choice is remembered on the device, and the selector hides in the pure 3D view.

  • Customise schematic line weight and track coloursAvailable now

    The schematic centreline is thicker by default and its weight is adjustable, and every per-state track colour — straight, curve, turnout/crossing/slip, structure, selected, validation error, warning/off-board, incompatible, and the placement ghosts — is customisable, with a reset to defaults. Settings live on the in-app profile (Schematic appearance), sync to your account, and are mirrored on the device so they follow you and work offline. The colours apply across the 2D views; the weight is schematic-only.

  • Realistic 3D rails and sleepersAvailable now

    In the 3D (perspective) view each placed piece now carries a genuine superstructure on top of its bed — two raised steel rails at the system's real gauge plus timber sleeper ties spaced at the system's tie pitch — instead of the plain extruded bed. Rails and ties are built as one or two merged meshes per piece, so the detail stays cheap even on a large layout. The 2D footprint and schematic views are unchanged.

  • Metric / imperial unit displayAvailable now

    A metric ⇄ imperial toggle in the status bar switches every length the planner shows — the cursor readout, inspector dimensions, catalogue tiles, scale bar, validation messages, and the New-layout / snap-step / deck-elevation inputs — between millimetres and inches. Metric stays the single canonical stored & computed unit (all geometry, snapping, and exports remain mm); imperial is a pure display derivation, parsed back to mm on input. The choice is a per-user preference (defaults to metric), synced to the account when signed in and remembered on-device otherwise. Angles (°) and grade (%) are unitless and read the same in both.

    Read the docs
  • Auto-snap to connectors with angle alignmentAvailable now
  • Ghost-piece preview with rotation while placingAvailable now
  • Overlap detection between piecesAvailable now
  • Refuse to place overlapping tracksAvailable now

    The candidate ghost turns red and the click is refused when a placement would land on top of an existing piece it isn't joining, with an on-canvas hint naming the reason ("Overlaps an existing piece") so the refusal isn't silent. Pieces about to be snap-joined or close-joined (loop closures) stay allowed. Dragging an already-placed piece is gated the same way — the dragged piece turns red and the drop is refused (it snaps back) when it would overlap a piece it isn't joining. Laying a run stops at the first piece that would cross existing track: that piece previews red and nothing past it is laid.

  • Selection, rotation, and deletion of placed piecesAvailable now
  • Keyboard shortcuts for common actionsAvailable now
    Read the docs
  • Undo historyAvailable now
  • Redo supportAvailable now
  • Multi-select and group operationsAvailable now

    Shift+click adds a piece to the selection (or removes it if already in); a plain click replaces the selection. Left-click drag on empty ground draws a marquee rectangle that selects every overlapping piece on release (shift extends instead of replacing). Left-click drag on a selected piece moves the whole selection together as one undo step — single-piece drags also run the connector snap so a release near a free connector lands on the join with the correct rotation. R / Shift+R rotates every selected piece by ±15° in place; Delete / Backspace removes the whole group. Escape backs out one step — closes any open dialog, then exits place mode, then clears the selection.

  • Hold Shift to constrain a move to one axisAvailable now

    Holding Shift while dragging a placed piece (or a multi-selection) locks the move to a single axis — the one the cursor has travelled farthest along, switching live if you reverse direction — so you can nudge a piece straight along the bench without drifting sideways. A dashed guide line through the grab point marks the locked axis. Works the same in the 2D editor and the 3D verification view. Shift at the start of a press still extends the selection; the lock only engages once a drag is in flight.

  • Drag pieces directly from the catalogue panelAvailable now

    Press on a catalogue card and drag onto the canvas; release commits the placement in one motion with the same snap / closure / overlap behaviour as click-to-place. The existing two-click flow (click card, click canvas) still works. Releasing on the dock or off-canvas cancels the drag without placing.

  • Catalogue compatibility hint against the selected pieceAvailable now

    When a piece is selected on the canvas, the catalogue cards visibly mark which pieces can mate with it (green-tinted border + corner dot) and which can't (faded). Driven by strict connector_type matching, so transition pieces stay surfaced from either side.

  • Canvas compatibility tint against the queued pieceAvailable now

    The reverse signal: when a catalogue piece is queued for placement, every existing placed piece that can't accept it (no shared connector type) renders in muted red. Selection still wins, so an explicitly clicked piece stays orange.

  • Start with an empty canvasAvailable now

    The new-layout welcome panel now offers a third option alongside "Build from a starter set" and "Try the demo oval": skip the picker and start placing pieces directly. The starter-set dialog stays reachable from the top bar and command palette for users who change their mind.

  • First-piece coaching for an empty boardAvailable now

    The welcome panel leads more clearly with the starter-set flow — tell the planner which set you own and it shows what you can build with it. And once the panel is dismissed, a quiet hint points a first-time builder at the catalogue dock to place their first piece, so an empty board isn't a dead end; the hint clears the moment a piece lands.

  • Name every layout when you create itAvailable now

    Every "New layout" entry point — the home page (signed in or anonymous), the layout switcher, the command palette, and the website's layout list — now routes through the same dialog, which asks for a name (and a baseboard size) before creating. The home buttons used to create an unnamed "Untitled layout" straight away; they now prompt first, so a new plan starts with a name you chose.

  • In-canvas mirror flip for symmetric piecesAvailable now

    Shift+F mirrors the selected placement (or the ghost while in place mode) across the piece's local X axis, swapping a curve's hand or a left turnout into a right turnout. The inspector's Properties panel exposes the same toggle as a Mirror flip row, and the flipped state persists to the layout's DB row so refreshes preserve handedness. One SKU, two hands — the catalogue no longer needs separate left and right entries for symmetric pieces.

  • Right-click contextual menuAvailable now

    Right-click on a placed piece opens a menu with Rotate clockwise / counter-clockwise, Mirror flip, and Delete — applied to the whole multi-selection when the right-clicked piece is part of one, otherwise scoped to just that piece (selection auto-replaces to match). Right-click on empty ground opens a layout-level menu (Select all, Clear selection, Toggle grid, Toggle polarity). Arrow-key + Enter navigation; Escape or an outside click dismisses. The previous right-click-to-pan camera binding has been retired — pan now lives exclusively on middle-mouse drag and on the Pan tool's left-click drag.

  • Group rotation around a shared centroidAvailable now

    Rotating a multi-selection now treats the group as a rigid body. Each piece's world position rotates around the selection's centroid before its heading bumps by the same delta, so the layout's internal structure is preserved. Single-piece rotation is the same shape (centroid of one point = the point) and keeps its previous spin-in-place feel. Joins straddling the rotated set drop in the same undo step; joins internal to the set survive. R / ⇧R and the right-click Rotate menu items both run through this path.

  • Choose the rotation pivotAvailable now

    The pivot is now the selection's free connector (any connector with no join attached) closest to the cursor. For a right-click rotate, that's where you clicked; for the R / ⇧R shortcut, it's wherever your mouse currently is on the canvas. Single-piece rotation finally lets you choose which end spins — cursor near A spins around A, cursor near B spins around B. Falls back to the centroid only when the cursor is off the canvas or no piece has a free connector (e.g. fully closed loops).

  • Jump to a joined neighbour from the inspectorAvailable now

    When a placed piece has a join on either endpoint, the inspector's selection card now shows the joined neighbour's name as a clickable link under the endpoint status. One click replaces the selection with the joined piece, so you can walk a chain end-to-end from inside the inspector instead of hunting pieces down on the canvas.

  • Inspector grouped into context zonesAvailable now

    The right-side inspector is split into three labelled zones instead of one flat accordion list: Selected piece (the selection card and its Properties), Whole layout (Validation, Bill of Materials, Layout Stats, and Polarity & Wiring), and Authoring (Decks and Layers). The panels and their behaviour are unchanged — they just read as grouped blocks so what's selected, what describes the whole plan, and what controls how the layout is organised are visually separate.

  • Collapse the inspector panelAvailable now

    A chevron in the inspector header minimises the panel to a thin rail, handing its width back to the canvas; the rail's chevron brings it back. The collapsed state is remembered per browser, so a panel you minimised stays minimised across reloads. The panel keeps its resizable width — collapsing and expanding doesn't lose it.

  • Cursor-side auto-flip on placementAvailable now

    While the placement ghost is snapping to a free connector, the cursor's side of the snap tangent decides which hand the ghost takes — drag above for a left-hand curve, below for a right-hand one. Same rule for turnouts. Straights and other symmetric pieces are left alone. A toggle in the top-right canvas chrome opts out when you want manual Shift+F control; defaults to ON.

  • Insert many — drag to lay a run of trackAvailable now

    With the Place tool armed and a straight or curve queued, press and drag on the canvas to lay a whole run at once. Each piece previews as a ghost that snaps onto the previous one's open end the way you'd drop track by hand — so every ghost becomes the anchor for the next; dragging out adds pieces, pulling back removes them (straights make a line, curves make an arc), and the chain's open end shows a connector dot. Press near a free endpoint and it continues that track; from empty space the first piece keeps its previewed (flat) orientation and the run extends along it toward the cursor, so a straight stays straight instead of swinging to the drag angle. Release commits the run in a single undo step. A plain click (or a drag shorter than half a piece) still places one. Turnouts, crossings, flex, and ramps keep one-per-click.

  • Visible "Checks performed" panel in validationAvailable now

    The validation accordion now lists Closure, Minimum radius, Clearance, and Grade as separate rows under the issues, each with a pass / fail / skipped badge. Failing rows show the issue count; skipped rows show the reason ("pick a reference train", "no joins yet", "no graded pieces yet"). Makes it explicit what the validator inspected rather than reading silence as nothing-to-check.

  • Baseboard size + visible outline + off-board validationAvailable now

    The New layout dialog now takes a baseboard width and depth in cm (range 20–1000, defaults 200 × 100), stored as mm under the hood. The canvas draws the rectangle as a subtle wood-tinted outline in both 2D and 3D so the user can see what they have to fit. A new validation check (Baseboard bounds) flags any piece whose footprint extends past the edge with a yellow-band 'Piece off baseboard' issue, surfaced in the issues list, the Checks-performed panel, and on the canvas itself — off-board pieces are tinted amber so they read at a glance, not just in the panel. Modular benches that intentionally overhang aren't blocked — the validator just warns. Polyline / non-rectangular baseboards are a future track.

    Read the docs
  • Custom baseboard colourAvailable now

    The board-colour picker — in both the New layout dialog and the layout-settings popover — gains a custom-colour swatch alongside the wood and neutral presets. It opens the system colour picker (with hex entry) for any colour, previews live on the canvas, and saves with the layout. The custom tile reads as active whenever the board's colour isn't one of the presets.

  • Layout switcher in the top-leftAvailable now

    Clicking the layout name opens a dropdown of every other layout the user owns (most-recently edited first), plus '+ New layout…' and 'Browse all layouts…' entries. The previous click-to-starter-set behaviour moved to the command palette and the empty-canvas welcome panel — switching between layouts in the editor is now one click instead of a trip back to the list page.

  • Per-user preferred scales + \ shortcut to overrideAvailable now

    Profile Preferences exposes a multi-select chip group (All / HO / OO / N) that persists to the user record. When set, the editor's catalogue dock shows only the picked scales — pick HO + OO and the N picker hides until you change the preference. Pressing ⇧S in the editor temporarily shows all scales for the current view (session-only, doesn't touch the saved preference); pressing again restores the filtered view.

  • Scale-aware reference-train dropdownAvailable now

    The validation accordion's reference-train picker filters by the focused piece's scale. Select an HO piece on the canvas (or highlight one in the catalogue) and N-scale and OO-scale trains drop out of the dropdown; if the current selection no longer fits, the dropdown auto-switches to the first remaining match so the validator stays consistent with the user's focus.

Track Catalogue

Hobby track systems modelled with accurate dimensions and connectors. The current focus is Hornby OO — additional brands and scales will follow.

  • Hornby OO basic tracks — straights, curves, turnoutsAvailable now
  • Brand and scale filtering UIAvailable now
  • Mixed-scale layoutsAvailable now

    Layouts no longer commit to a single scale at creation. The new-layout dialog only asks for a name; the layout's scale is then derived from the pieces you actually place. A bench that mixes N and HO modules reads as 'Mixed' in the library; benches with one scale show that scale's tag. Connector compatibility still prevents physically wrong joins.

  • Data-driven scales — no hard-coded HO/OO/NAvailable now

    Any scale the catalogue defines now works end to end with no code change. The editor's status-bar ratio reads straight from the scale record (so a new scale shows its real 1:xx instead of a blank), the catalogue's scale picker lists each scale with its ratio and track gauge instead of just a two-letter code, and the preferred-scale filter on the website profile and the in-app profile lists whatever scales the catalogue actually carries — all fed from one query rather than a fixed HO/OO/N list. The in-app filter keeps a small static fallback so it still works offline. User-defined custom scales (your own ratio + gauge) are a separate, later step.

  • Complete Hornby OO range — crossings, buffer stops, pointsIn progress
  • Märklin C-Track (HO) — accurate geometry and real SKUsPlanned

    Märklin entries currently in the catalogue are placeholders; the real C-Track geometry and product references are not yet wired up.

  • Other 2-rail HO brands — Roco GeoLine, Peco Streamline, Kato Unitrack, Fleischmann ProfiPlanned
  • Additional scales beyond OO — N, Z, O, GPlanned

    Brand and scale selectors already list these, but they have placeholder data. Real geometry and SKUs are still to come.

  • Crossings, buffer stops, and uncoupling tracksPlanned
  • Use every piece an imported catalogue definesAvailable now

    An audit of the XTrkCAD import pipeline found pieces that get parsed and stored but never reach the editor — they hydrate as real geometry yet were hidden from the catalogue dock (and silently dropped from a layout on load). The fix was to surface and render what we already import. Every imported track type is now a placeable catalogue category: diamond crossings, double slips, buffer stops, accessory track (re-railers, uncouplers, cross-system transitions), and turntables — each surfaced under its own tab, rendering the geometry it was imported with. Structures — buildings and roundhouses — are placeable too (see below). Route selection now covers the multi-route pieces as well: a double slip's four routes (and a turnout's) are thrown by their real catalogue names, in the inspector and the right-click menu, driving polarity and the rail overlay.

  • Place buildings and scenery from the catalogueAvailable now

    Structures shipped inside an imported parameter file — buildings, sheds, roundhouses, platforms — are now placeable. They show up under their own Structures tab in the catalogue dock and drop freely wherever you click: no connector snapping, because a structure is an outline for clearance and context, not track. Each renders its real footprint outline, rotates and mirrors with the same controls as track, and warns when it pokes past the baseboard. The inspector reads back its width and depth. They save and load with your layout like any other placed object.

    Read the docs
  • Turntables and transfer tablesPlanned
  • Links from catalogue items to manufacturer product pagesPlanned
  • Buildings, scenery, and vehicles in the catalogueFuture idea
  • Community-submitted catalogue items with admin reviewFuture idea

Electrical Polarity & Wiring

Catch reverse-loop short circuits before you spend money on track. The planner traces your wiring and flags conflicts visually.

  • Polarity solver via graph traversal (2-rail DC)Available now
  • Live conflict count in the toolbarAvailable now
  • Polarity overlay — red/blue rails, amber for conflictsAvailable now
  • Electrical-system awareness — skip 3-rail AC tracksAvailable now

    Märklin C-Track and Lionel FasTrack use 3-rail AC, where both running rails are at the same potential. The solver excludes them so they don't trigger phantom conflicts, and the toolbar button is disabled when the entire layout is 3-rail.

  • Per-conflict explanations and suggested fixesAvailable now

    Polarity solver now returns a per-conflict descriptor — affected pieces, the specific joins it blamed for the contradiction, a plain-English reason, and a suggested fix that names insulated rail joiners directly. A new Polarity & Wiring inspector accordion lists every detected conflict; clicking a row jumps the canvas selection to the first offending piece, and the panel header doubles as a one-click toggle for the canvas overlay.

  • Route state for turnouts and slipsAvailable now

    `planner.placements.turnout_state` is wired end-to-end and keyed on a piece's real catalogue route names, so it covers every switchable piece — a turnout's two routes and a double slip's four. The polarity adapter drops every join touching an inactive route, so a turnout thrown to one exit (or a slip set to one diagonal) no longer electrically connects what's wired to a route it isn't carrying; fixed diamond crossings stay fully live. UI: a route pill in the Properties accordion listing the piece's routes by name, plus a right-click 'Set route' submenu with current-route badges (mixed selections show N/M; all-on-one-route shows 'current' and disables the no-op click). The polarity overlay paints only the active route's rails so the visual story matches the electrical model.

    Read the docs
  • Insulated rail joiner placementAvailable now

    Migration 0045 adds is_insulated to planner.joins; the schema, query layer, hydration, store, auto-save, and polarity adapter all carry the flag through. Toggling a join's insulation flips the electrical behaviour immediately — a reverse loop with one insulated join reports zero conflicts, exactly like a real insulated joiner on the bench. UI: a small pill next to the inspector's jump-to-neighbour link (dashed em-dash off, filled orange ‖ on), plus a right-click 'Insulated joiner at <connector>' entry per joined endpoint on single-piece right-clicks. The polarity overlay paints a short perpendicular amber bar at each insulated join.

  • Auto-reverser placement suggestionsFuture idea
  • 3-rail wiring assistantFuture idea

    Visualize the live center-stud network for Märklin / Lionel layouts, flag missing feeders on long runs, and check turnout frog polarity.

Saved Layouts & Persistence

Your layout sticks around between sessions and syncs to your account so the whole portfolio follows you between devices.

  • Auto-save to the browser (localStorage)Available now
  • Automatic migration of older saved layoutsAvailable now
  • Cloud-backed saved layouts for signed-in usersAvailable now

    Layouts persist to Postgres via saveLayoutAction, with useAutoSave debouncing writes every 2 s. The top-bar save indicator surfaces dirty → saving → saved transitions, and the cloud-quota meter on /profile shows the per-tier cap.

  • Multiple named layouts per accountAvailable now

    Signed-in users see their full layout library at /, with a card per layout, the New layout dialog, in-place rename, and delete-with-confirm. The list page is the planner's home for authenticated users.

  • Layout list page with thumbnailsAvailable now

    Each card on the signed-in home page now shows a small SVG preview of the layout's centerlines, with the card's text colour driving the stroke via currentColor. Layouts that have never been saved with pieces fall back to an "Empty layout" placeholder. Thumbnails are precomputed by the editor's auto-save and persisted on the layout row, so the list page doesn't re-fetch geometry per card.

  • Auto-generated layout previewsAvailable now

    The same precomputed SVG covers the layout-list thumbnail need. Richer previews (rendered piece sprites, ballast / scenery shading) remain a future option once the renderer supports it.

  • Layout version historyFuture idea

Install & Offline

Install Locodex Planner as an app on desktop and tablet, and keep designing when the connection drops.

  • Install as an app on desktop & tabletAvailable now

    An Install app button in the landing nav and the bench header installs the planner as a standalone app, with its own window and a home-screen / dock icon. On Chrome and Edge (desktop and Android tablets) it triggers the browser's native install prompt; on iPad and iPhone Safari it shows the Add-to-Home-Screen steps, and it hides itself once the app is installed. Backed by a web app manifest, generated brand icons, and a service worker. This first step is installability only.

    Read the docs
  • Work offline — design with no connectionAvailable now

    Open and edit your layouts with no connection. The editor renders client-side: it loads the layout, the track catalogue, and your account context from the server when online and from an on-device cache (IndexedDB) when offline, and the installed app's service worker serves the editor offline. Caches warm as you work online, so any layout you've opened is available offline next time; edits save locally first and sync to the cloud when you reconnect. Online behaviour is unchanged — the live route still serves when connected.

    Read the docs
  • Pro stays unlocked offlineAvailable now

    Your one-time Pro (eternal) license keeps working with no connection. The website issues a signed license token (ES256); the installed app stores it on-device and verifies it locally against an embedded public key, so Pro modules and capabilities stay unlocked offline — never gated behind a live server check. The token refreshes whenever you're online and signed in, and clears on sign-out. The on-device check can only upgrade you to Pro, never downgrade a license the server already confirmed.

Accounts & Tiers

Two products: buy-once Pro software (the eternal license) and an optional Cloud service (Free / Hobby / Club) for syncing your plans across devices. Pay once, subscribe, both, or neither.

  • Use the planner without an accountAvailable now

    The full editor is usable with no sign-in. The app opens to a home of your on-device layouts; New layout creates one locally and opens it — no account, no network. Layouts are saved on the device and work offline. Signing in is an optional upgrade that adds cloud sync and backup across devices; it's never required to plan. The installed app opens straight to this home rather than the marketing page.

  • User registration and loginAvailable now

    Both the encyclopedia and the planner host their own register / login pages now, posting to the shared @locodex/auth backend. Same users table, same JWT, same SSO cookie — only the form chrome is per-app, so each surface keeps its own visual identity.

  • Single sign-on across encyclopedia and plannerAvailable now
  • Public landing page for visitorsAvailable now

    Signed-out visitors at planner.locodex.app see a marketing landing — hero with an animated snap illustration, value props, the manufacturer catalogue, and an inline auth panel that posts to the planner's own auth actions.

  • Sign in with GoogleAvailable now

    Both apps now offer Google as an OAuth option in addition to email + password. Shared @locodex/auth helpers verify Google's ID token; the same users table backs both flows. If a Google account's verified email matches an existing user, the OAuth identity is auto-linked.

  • Profile pageAvailable now

    Standalone /profile route reachable from the UserPill menu. Shows a builder's card with the user's name, tier, and aggregate stats (layouts, pieces placed), plus tabs for the layout library (with scale / cloud / local filters and a cloud-quota meter), the current plan, and editor preferences (default scale, units, theme). Account management is wired too: rename your display name, change your password (email accounts), export every layout as one .trackplan bundle, and open a .trackplan from disk straight into the editor.

  • In-app account pageAvailable now

    The installed planner app hosts its own /profile route (reached from the user menu) instead of bouncing to the website: an account summary showing identity, Cloud tier with synced-layout usage, and Pro license status, plus sign out. It reads /api/entitlements when online and falls back to the token identity offline. Billing, plan changes, and account edits stay on the website and open one click away — the app deliberately doesn't reimplement them.

  • Cloud service — Free, Hobby, ClubAvailable now

    The subscription is the Locodex Cloud service and governs cloud features only — how many layouts sync. Free gets 1 plan, Hobby 10, Club a soft 100 (warns, never blocks). Everything else in the editor — the full catalogue, every core module, unlimited track pieces, export and share links — is free on every tier. Tier resolution reads the user's active subscription row and supports the full lifecycle (active / trialing / past_due / canceled / incomplete).

  • Pro software unlocked by the eternal licenseAvailable now

    The one-time eternal license unlocks the grow-into capabilities, independent of any subscription: pro modules (e.g. the planned Sectioning & Blocks), multi-level layouts (decks, gradients, helix), and loco-aware validation (clearance + min-radius vs a chosen train). Free keeps the flat single-level editor, every core module (Polarity & Wiring), and basic validation. Locked pro features show in the editor with a lock and a one-click route to /pricing. The subscription deliberately doesn't grant any of this — it governs cloud sync alone.

    Read the docs
  • Public pricing pageAvailable now

    Marketing surface at /pricing for the Cloud service (Free / Hobby / Club) and the one-time eternal license that unlocks Pro. Monthly / annual billing toggle, a license-owner discount toggle that previews the cloud-only rate, a side-by-side comparison table, and a six-question FAQ.

  • Subscriptions + licenses schemaAvailable now

    subscriptions and licenses tables in the shared DB schema. Subscriptions carry tier, billing period, status (active / trialing / past_due / canceled / incomplete), period boundaries, cancel-at-period-end, and nullable payment-provider linkage. Licenses are once-per-user-per-kind, with the eternal license as the first kind. resolveTier now reads the active sub and the profile page's billing card renders real cycle data.

  • Cloud-layout cap enforcementAvailable now

    newLayoutAction now refuses to create the (N+1)th cloud layout for a user already at their tier's cap. Refusal returns a typed result the dialog renders as an explicit "you're at your cap" panel with a routed upgrade CTA. The layout list header shows a live "X / Y cloud plans" pill that turns orange at cap, and the profile page's upgrade buttons now route to /pricing instead of staying disabled.

  • Unlimited track pieces on every tierAvailable now

    Track-piece count is no longer a paywall — it's local compute, not a cloud cost, so every tier builds layouts of any size. The editor store's per-tier cap mechanism stays in place as a dormant safety net, configured to unlimited; a future limit could be reinstated in TIER_CONFIG without touching the enforcement code, banner, or server defence.

  • Catalogue is open to every tierAvailable now

    The full rail catalogue is open to every tier. After the cloud / license split, subscription tiers differ on a single axis — the cloud-save quota (maxSavedLayouts). Track pieces, export, and share links are free for everyone; pro modules are unlocked by the eternal license, not by any subscription.

  • Dev-only subscription shortcutsAvailable now

    A Dev tools card on /profile (gated server-side on NODE_ENV === "development") flips subscription tier, billing period, cancel-at-period-end, and eternal-license state without going through a payment provider. Backed by mutation primitives in @locodex/db (upsertActiveSubscription, cancelSubscriptionAtPeriodEnd, clearActiveSubscriptions, grantLicense, revokeLicense) — the same seam the future Stripe webhook handler will write to.

  • Stripe Checkout for the Hobby & Club subscriptionsAvailable now

    The /pricing CTAs for the Hobby and Club Cloud tiers route through a Stripe-hosted Checkout Session created server-side from createCheckoutSessionAction. The action carries the user's id via client_reference_id so the webhook handler can resolve the right account on completion; price IDs come from the STRIPE_PRICE_HOBBY_MONTHLY/ANNUAL and STRIPE_PRICE_CLUB_MONTHLY/ANNUAL env vars; Stripe-side products + prices are configured per the Stripe setup walkthrough. When the STRIPE_* env vars aren't set the action returns a clean error and the dev shortcuts stay the working path.

    Read the docs
  • Subscription management portal (Stripe Customer Portal)Available now

    The Manage billing button on /profile (Account & plan tab) now opens a real Stripe Customer Portal session via createBillingPortalSessionAction. Plan switching, payment-method updates, and invoice history live in the portal — the planner doesn't reimplement those (cancellation is now a direct in-app action instead). The button is disabled until the user has a subscription with a provider_customer_id (set by the webhook on checkout completion); dev-shortcut subs intentionally don't enable it because they have no Stripe customer behind them.

  • Cancel your subscription in-app (instant, reliable)Available now

    The Cancel subscription action (on /profile, /profile/plan, and the /pricing Free card) cancels through a write-through server action instead of bouncing to Stripe's hosted portal: it flags cancel_at_period_end on Stripe and persists the flag to our own subscription row in the same request, then revalidates. The account flips to 'until DATE' the moment the action returns, with no wait on the async customer.subscription.updated webhook or a redirect carrying ?billing=success — the gap that previously left a cancelled sub still showing 'renews' with the cancel button live (and re-clicking hit Stripe's 'already set to be canceled at period end' error). The Stripe update is idempotent and the webhook still reconciles the same fields when it lands. A confirmation dialog guards the now-instant action.

  • Choose which plans keep syncing when you downsizeAvailable now

    Cancelling to Free (1 synced plan), or downgrading to a tier whose cap is smaller than your plan count (e.g. Club → Hobby), now prompts you to pick which plans keep syncing. The ones you keep stay editable; the rest turn read-only — still backed up and exportable to .trackplan, and editable again the instant you resubscribe (a per-layout frozen flag on the layouts table). Read-only is derived: a frozen plan only locks once the account is actually over its cap (isLayoutSyncFrozen), so the choice made at cancel time doesn't bite until the period ends, and deleting plans back down to the cap auto-unlocks the rest. The editor shows a read-only banner and suppresses autosave, the layout list + library badge frozen plans, and save / rename / layout-settings refuse server-side. Upgrades / fresh checkouts clear the freezes.

  • Webhook handlers for Stripe subscription lifecycleAvailable now

    A new POST /api/stripe/webhook endpoint verifies stripe-signature and handles checkout.session.completed (insert subscription row with provider linkage), customer.subscription.updated (status + period + cancel flag sync), customer.subscription.deleted (mark canceled), and invoice.payment_failed (mark past_due). Every handler writes through the same upsertActiveSubscription / updateSubscriptionByProviderId primitives the dev shortcuts use, so the DB seam is unified. Unknown event types acknowledge with 200 so Stripe stops retrying; handler errors return 500 so it does. 14 unit tests cover the guard rails and each lifecycle branch.

  • Stripe Checkout for the €50 eternal licensePlanned
  • Trial period for new Hobbyist / Pro usersFuture idea

Export & Sharing

Take your layout off the screen — print it, send it to a friend, or bring it to the hobby shop.

  • Export layout as PNG imageAvailable now

    Standard manufacturer-style track plan: each piece is a filled track footprint with visible bed width — straights as rectangles, curves as ring segments, turnouts composite — and adjacent pieces touch with their own outlines forming the visible boundary between them. The manufacturer SKU is rendered at each piece's centroid so the printed plan stands on its own. Horizontal and vertical dimension lines with metres labels frame the layout, and a bottom-right title block carries the layout name and piece count. Rasterisation runs entirely in the browser.

  • .trackplan portable file format (export + import)Available now

    Versioned JSON format that captures everything needed to reconstruct a layout — placements, joins (with insulated rail joiners), layers + memberships, baseboard, board colour, snap step, plus turnout route state. Pieces are referenced by manufacturer system + SKU rather than internal id, so a file survives catalogue reseeds and moves between machines or accounts. Export from the editor's top-bar menu (or the command palette); import on the layout list page via a file picker. Strict on import — refuses the file (rather than silently dropping pieces) when a SKU isn't in the target catalogue.

    Read the docs
  • Back up & restore your whole library (.zip)Available now

    Profile → Data & account exports every layout you own as one .zip — a standalone .trackplan per plan plus a manifest.json index — and restores it the same way. Import unpacks the archive and recreates one layout per file, respecting your Cloud plan cap and stopping cleanly when the next plan would exceed it; per-file problems (unknown pieces, wrong version) are reported without aborting the batch. Because each plan in the zip is an ordinary .trackplan, you can also unzip and import a single layout on its own.

  • Change your Cloud plan in-app (prorated upgrades, deferred downgrades)Available now

    Subscribers manage their plan on a dedicated /profile/plan page instead of being bounced to the marketing pricing page. Upgrades (to a higher tier, or monthly → annual) apply immediately and are charged the prorated difference — previewed as 'you'll be charged €X today' before you confirm. Downgrades keep your current plan entitling until the period ends, then switch over (tracked on the subscription so the Account tab shows the scheduled change). The billing-cycle line reads 'until DATE' once a cancellation is scheduled, and the cancel action disappears while a cancellation is already pending.

  • Import custom pieces & environment items with your own 3D modelsPlanned

    Bring your own track pieces and scenery: a portable, open .locoasset definition (semantic geometry + connectors for track, a clearance footprint for buildings / environment) paired with a .glb 3D model, packaged into a self-contained .trackplan bundle so a shared file carries everything needed to open it offline. The .glb is the visual skin; snapping, length/radius, polarity, and clearance are driven by the declared geometry, never the mesh — so environment items act as real clearance obstacles, not just decoration. The .locoasset structure is open so anyone can author assets; the .trackplan structure stays proprietary. Design recorded in .claude/docs/custom-assets-and-bundles.md.

  • Export layout as a true-scale (1:1) printable PDFAvailable now

    Export the plan as a 1:1 PDF — 1 mm of track prints as 1 mm on paper — tiled across pages with overlap and corner registration marks so a layout bigger than a sheet still prints true-size. Each footprint draws at its system's real sleeper-base width, so trimmed-and-taped sheets become a build template you lay real plastic track onto. A calibration ruler on every page lets you confirm your printer didn't fit-to-page (print at 100% / actual size). Reuses the same footprint geometry as the on-screen plan, generated entirely in the browser.

    Read the docs
  • Parts list export with quantities and SKUsAvailable now

    The top-bar Export menu (and the matching command-palette entry, plus the Export CSV button inside the inspector's Bill of Materials accordion) downloads a CSV with one row per distinct piece — SKU, name, brand, system, quantity, unit price, line total, currency — sorted brand → system → SKU for shopping. Spreadsheet-ready out of the box: RFC-4180 quoting, CRLF line endings, UTF-8 BOM so Excel under Windows opens accented names correctly. Synthetic / unmatched placements are counted in a footer note so the user knows when the total is partial.

  • Parts list with estimated cost totalsAvailable now

    The same parts-list export carries per-piece line totals plus a trailing summary row per currency (one row per currency when a layout mixes brands across regions). The inspector's BoM accordion shows the same headline total live, and updates as you place or remove pieces.

  • Parts list grouped by scaleAvailable now

    Mixed-scale layouts split into one section per scale in both the inspector accordion and the CSV export — each with its own piece count and per-currency subtotal — so a bench mixing HO and N modules reads as two coherent shopping lists instead of one mashed-together pile. Single-scale layouts skip the section chrome entirely and keep the flat layout.

  • Read-only share links (public URLs)Available now

    Each layout can be exposed at /share/<token> by the owner from a new Share button in the editor's top bar. Anyone with the link (no Locodex account needed) sees a read-only viewer rendering the same R3F canvas as the editor — pan, zoom, 2D / 3D camera toggle, baseboard, joins — wrapped in a stripped-down top bar with a 'Made with Locodex' link. The token is a cuid2 persisted on planner.layouts.share_token (nullable, unique-indexed); enabling sharing issues a fresh token, disabling clears it. Re-enabling after a disable deliberately mints a new URL so the old one stays dead. The viewer doesn't share the editor shell — it reuses CanvasScene directly so there's no tool rail, catalogue, inspector, auto-save, or keyboard-shortcut listener in the read-only tree. A shared link now previews richly when posted to a forum or social — the layout's name in the title and its own thumbnail as the link image — and Share is reachable from the command palette as well as the top bar.

    Read the docs
  • Embed a layout in a forum post or blogFuture idea
  • Export to CAD-friendly formats (DXF, SVG)Future idea

Advanced Layouts

Multi-deck layouts, ramps, helixes, and free-form flex track — the things real layouts actually need.

  • Multi-deck layouts (ground level, elevated, underground)Available now

    Decks are now a first-class concept, distinct from layers. Each layout has 1+ named decks with an elevation in mm; every placement belongs to exactly one deck, and the deck's `baseElevationMm` drives the piece's Z. Editing a deck's elevation rebases every piece on it in one undo step. Layers are back to being pure visibility/colour-coding tags — a piece can sit on the Mezzanine deck and be tagged Mainline + WIP without conflict. The `.trackplan` format bumps to v3 to carry decks as a top-level field; v1 and v2 files still load (v2's layer-elevations migrate into derived decks at parse time). Cross-deck connections via ramps / helixes and grade-aware validation are tracked as separate items.

    Read the docs
  • Per-layer visibility, colour coding, and assignmentAvailable now

    The Inspector's Layers accordion is a real per-layout list now — add / rename / recolour / delete, plus an eye toggle that hides every piece whose layers are all hidden. Pieces in two layers stay visible as long as one of them is, so a 'Hidden' layer can hide a working subset without yanking pieces that belong to another active layer too. A toggle at the top of the accordion turns colour coding on or off; with it on, each piece is tinted by its primary layer's colour. Right-click on a piece (or multi-selection) shows 'Move to layer' and 'Also add to layer' lists for one-click reassignment.

  • Cross-layer connections via ramps and helixesIn progress

    Ramps (inclined straights) and helixes (curved climbs) are both placeable catalogue pieces now, under the Ramp section of the Hornby OO set — connector A at z=0, connector B lifted to z=rise, so the existing connector-type snap engine mates them to higher-deck pieces without any new rules. The 3D body tilts (ramp) or spirals (helix) to match the climb; the Inspector shows length/radius, rise, grade %, and pitch; and the grade check flags a piece steeper than your trains can pull (measured over arc length for helixes, so a full turn counts even though it returns to the same XY). You can also incline track you already own: select a connected run of plain straights/curves, type a grade % in the Inspector, and the run becomes a continuous climb — straights turn into ramps, curves into helixes, and the elevation restacks along the join chain so the pieces stay mated (anchored end fixed; type 0 to level it back). Cross-deck bridging is automatic too: drag a plain straight/curve so one end snaps to a lower-deck piece and the other reaches a higher-deck connector, and the planner inclines it to the deck-height difference and joins both ends in one drop (refusing — with an amber hint — when the climb would exceed 25%). The seeded ramp SKUs are synthetic proofs-of-concept; real manufacturer incline/helix data comes next.

    Read the docs
  • Elevation-aware geometry checksAvailable now

    The validator now checks a piece's grade against the trains you're building for: a ramp that climbs steeper than your loco can pull is flagged red. Train-relative (not a fixed percentage) — it uses the strictest max grade across your reference train or owned collection, so a layout that's fine for one loco can warn for another.

  • Flex track — draw a spline, generate flex segmentsFuture idea
  • Auto-generated benchwork outlineFuture idea

3D & Visualization

See your layout the way it will actually look — beyond the top-down view.

  • 3D preview of the placed layoutAvailable now
  • Split view — 2D editor and 3D preview side-by-sideAvailable now

    Resizable vertical split with a shared ghost-piece preview, so a candidate placement appears in both panes at once.

  • Wiring diagram view with feeder positionsFuture idea
  • DCC decoder address overlay for turnoutsFuture idea
  • Train operation simulation (run a loco around the loop)Future idea
  • Lighting and time-of-day previewFuture idea

Community & Content

Locodex is a hobbyist platform — the catalogue and templates get better when modellers contribute.

  • Send feedback / report a bug from inside the appAvailable now

    A 'Send feedback' link in the app-home footer and the editor's status bar (plus a 'Send feedback / report a bug' command in ⌘K) opens a short form right inside the app — no account, no leaving. Each submission carries the context that makes a bug actionable (app version, browser, current page, and the open layout's id) and is delivered to the team server-side, so beta testers never see a tracker or sign-in wall. After an export, a one-time NPS micro-survey ('how likely are you to recommend Locodex?') asks for a quick score, with an optional note that routes through the same feedback channel.

    Read the docs
  • In-app documentation at /docsAvailable now

    Every feature has a short, focused page under /docs — editor basics, the tool rail, snapping, joins, the .trackplan format, and a full keyboard-shortcut reference. Markdown-sourced (one file per topic in src/content/docs), grouped into categories, with a sidebar and prev/next links between pages. Updates ship in the same commit as the feature they describe, so the docs never drift from the running app.

  • Cross-links between /docs and /roadmapAvailable now

    Docs and roadmap items now reference each other through a shared id. Roadmap items gain an optional `id` field; doc frontmatter gains an optional `roadmapId`. When both sides agree, the docs index renders a status badge next to the page title, the doc page shows a 'View on roadmap' link, and the roadmap shows a 'Read the docs' link below the item. Status (shipped / in progress / planned / idea) stays single-sourced in roadmap.ts — docs never duplicate it.

  • Layout templates — pre-built ovals, figure-8s, and shelf plansPlanned
  • Encyclopedia link-out from catalogue itemsPlanned
  • Community-submitted layouts galleryFuture idea
  • Ratings and comments on shared layoutsFuture idea
  • Forks and remixes of public layoutsFuture idea

Collection & owned-aware planning

Tell Locodex which trains and track you own, and the planner plans around them — validating against your real trains and pricing only the track you still need to buy.

  • Your encyclopedia collection syncs to the plannerAvailable now

    Models you mark as owned in the encyclopedia flow through to the planner, along with a new track collection (the pieces you already have, counted per item). The groundwork everything below builds on.

  • Owned-track pricing — only price what you still needAvailable now

    Mark how many of each piece you already own right in the bill-of-materials, and the shopping-list price drops to just what's left to buy. A per-row stepper records what you have; the totals show the saving.

    Read the docs
  • Validate against the trains you actually ownAvailable now

    Min-radius and clearance checks driven by your collection instead of a generic per-scale default — the planner warns when a curve is too tight for a loco you own.

    Read the docs
  • Failing pieces marked on the canvasAvailable now

    Grade, clearance, and min-radius issues now mark the offending pieces directly on the canvas — in both 2D and 3D — instead of living only in the Inspector's issues list. A piece that fails a hard check (over-steep grade, fouling clearance, curve below min radius) is tinted a loud validation red; one that only trips a warning (a tight overpass) is tinted the same amber as an off-board piece. Both read off the exact issues the inspector lists — no second pass — so the canvas and the panel never disagree, and selection still wins so you can always see what you've picked.

  • Build a layout for a specific set of trainsAvailable now

    Pick the trains you want to run and the planner sets the scale, derives the strictest validation profile, and filters the catalogue to compatible track.

    Read the docs

This roadmap reflects intent, not promises. Priorities shift as we learn what hobbyists need. Items marked “idea” may never ship; items marked “planned” may change in scope or order.