Excalidraw Script Engine Coding Example: Building a Gallery View
Based on Zsolt's Visual Personal Knowledge Management's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.
The script builds a thumbnail list by filtering `app.vault.getMarkdownFiles()` using both folder-path matching and a case-insensitive `thumbnail` substring on the base filename.
Briefing
A working Excalidraw automation script can turn an Obsidian vault of YouTube thumbnails (and optional storyboard notes) into a clickable, grid-style “gallery view” on an Excalidraw canvas—without manually dragging images one by one. The core payoff is speed plus structure: the script scans the vault for thumbnail files, sorts them by creation time, pairs each thumbnail with its matching storyboard markdown (when present), then lays everything out in a 12-column matrix with consistent spacing and aspect-ratio-friendly sizing.
The workflow starts by creating an empty Excalidraw drawing and attaching Excalidraw Automate to the active window via the developer console. From there, the script derives the target folder path from the current file’s path and name, then builds a list of candidate thumbnail markdown files. It filters by two conditions: the file path must start with the computed folder path, and the file base name (lowercased for casing tolerance) must contain the word “thumbnail.” In the author’s vault, this yields 145 thumbnail-related markdown files.
Sorting matters because thumbnails need to appear in chronological order rather than arbitrary filesystem order. The script uses each file’s `stat.ctime` (creation time) and converts it into a Moment-formatted date string for comparison. After an initial reversal mistake, the sort logic is flipped so the oldest items come first—confirmed by checking the first element corresponds to an older video date.
Next comes the pairing step: for each thumbnail file, the script searches for a corresponding storyboard markdown file in the same folder. It does this by reusing the folder path and filtering for markdown files whose base name matches “storyboard.” If a storyboard exists, the script grabs the first match; if not, it keeps the storyboard value undefined using a safe optional approach.
The layout step is where the script becomes practical. It defines a grid placement function that computes row and column coordinates using modulo arithmetic (the grid uses 12 columns). Each thumbnail is added to Excalidraw with `EA add image`, then the image element is updated so its link becomes a markdown link to the storyboard file path—making each thumbnail clickable and navigable to the storyboard.
Asynchronous image loading introduces real-world friction. An initial attempt using a bulk “add elements” approach starts loading images in the background and can lead to timing issues, distorted sizing, and confusing console behavior. The final, reliable version switches to a sequential `for` loop that awaits each image load before placing the next one. That change prevents race conditions and results in a clean matrix where all images appear and links work.
By the end, the canvas contains the full gallery of thumbnails arranged in rows and columns, with storyboard links attached per image. The author’s takeaway is that Excalidraw Automate can be used to automate repeatable knowledge-management workflows inside Obsidian—turning a messy manual process into a deterministic script-driven layout.
Cornell Notes
The script automates a “gallery view” in Excalidraw by scanning an Obsidian vault for thumbnail markdown files, sorting them by creation time, and pairing each thumbnail with an optional matching storyboard markdown file. It computes a 12-column grid layout using modulo arithmetic and places each thumbnail image onto the Excalidraw canvas with calculated x/y coordinates, spacing, and dimensions. Each image is then updated so it links to the storyboard file path, making the gallery clickable. A key reliability fix is switching from bulk asynchronous placement to a sequential `for` loop that awaits image loading, avoiding race conditions and distorted output. This matters because it replaces manual drag-and-drop with a repeatable, vault-driven workflow.
How does the script identify which files count as thumbnails in the vault?
What determines the ordering of thumbnails on the Excalidraw canvas?
How does the script attach storyboard links to each thumbnail?
Why did the initial image placement attempt produce timing and sizing problems?
How does the grid layout math work (rows/columns)?
Review Questions
- What two filter conditions does the script use to build the thumbnail list from `getMarkdownFiles()`?
- How does the script use `stat.ctime` (and Moment) to sort thumbnails into chronological order?
- What change made the final image placement reliable: bulk async placement or sequential `for` loop with awaiting image loads? Explain why.
Key Points
- 1
The script builds a thumbnail list by filtering `app.vault.getMarkdownFiles()` using both folder-path matching and a case-insensitive `thumbnail` substring on the base filename.
- 2
Chronological ordering is achieved by sorting on each file’s `stat.ctime`, with Moment formatting used to interpret dates and a corrected comparator direction to show oldest-first.
- 3
Each thumbnail is paired with an optional storyboard markdown file by searching the same folder for a base name of `storyboard` and selecting the first match when present.
- 4
A 12-column grid layout is computed from the thumbnail index using modulo arithmetic, then translated into x/y coordinates with padding plus width/height terms.
- 5
Clickable behavior comes from updating each Excalidraw image element’s link to a markdown link pointing to the storyboard file path.
- 6
Reliability improves when image placement is done sequentially with a `for` loop that awaits image loading, avoiding race conditions seen in earlier bulk/asynchronous attempts.