myzettel
Org Roam is the foundation on which i have implemented my Zettelkasten . This page documents my implementation besides tracking how the system evolved and provides necessary references.
Motivation
After reading about Roam Research, i searched for an open source alternative and ended up at the ycombinator article . Reading that article led me to Org Roam which was built on top of Emacs .
Until then all my notes were in multiple formats scattered across a multitude of storage locations ranging from local hard disk to cloud services like GDrive and dropbox . Over a period of time, these notes became repetitive and disjointed, not easy to search and use/modify quickly.
I tested other paid and proprietary alternatives like roamresearch↗ and obsidian to get a feel of them. Since i wanted to avoid vendor lock, i went for this text based open source tool built upon Org Mode .
Concepts
Org-roam was built to enable note taking. Renaming notes, having multiple org-roam directories or a nested folder structure were considered as anti-features in original org-roam. In v2, With org-id renaming notes is fully supported along with nested folder structures. I however follow a flat single folder for maintaining my notes, irrespective of their subject.
I read the concept note1 from the org-roam author and the book How To Take Smart Notes referred by him before proceeding further.
Process
ox-hugo is used to export the content from org format to markdown format. Hugo generates a static website in HTML format from the markdown formatted content.
To capture ideas or notes, orgzly or Emacs is to be used with Org Roam into a journal entry. At eod or asap, these journal notes are to be organized into permanent notes.
In early Mar'26, the entire setup has been migrated to syncthing with the help of oci instance. lightweight Web Server (lWS) is used to serve this site on the Note6Pro , even when offline.
Implementation Details
On a quest to reduce dependencies as much as possible, decided to use hugo without theme.
The following scripts used in the site is available as literate programmes.
Git log for the current implementation can be found here↗ .
A text first site
The default site is lightweight in nature. Javascript is used minimally — for mobile navigation and on-demand features like the neighbour panel and graphical view. Dark mode is implemented entirely using CSS.
When the occasional js needs to be invoked, to ensure that text only browsers are not presented with non-functional buttons, the button definitions like below have been avoided.
<button
id="neighbour-trigger"
class="not-for-mobile not-for-graph-sidebar"
data-lnk="{{ $lnk }}"
data-ns-src="{{ $nsSrc }}"
aria-label="Show neighbours in graph"
>Neighbours</button>
Instead they are to be wrapped as follows:
<script>
document.write('<button id="neighbour-trigger" class="not-for-mobile not-for-graph-sidebar" data-lnk="{{ $lnk }}" aria-label="Show neighbours in graph">Neighbours<\/button>');
</script>
The relevant javascripts invariably begins with a guard:
const trigger = document.getElementById("neighbour-trigger");
if (!trigger) return;
Graphical view
A graphical view was added earlier to the site using D3-force based javascript along with a script for mobile menu handling. A third Javascript was added for search integration by slightly modifying the one from first version. To reduce potential performance issues, an intermediate lightweight layer i.e neighbour panel has been added. It shows the neighbour information as a linked list grouped by Links and Backlinks using the neighbour-loader.js . The entry to the graphical view is now available in this neighbour panel.
I have used org-roam-UI mode as a benchmark for the graphical view and almost the entire code was generated with assistance from Claude and Gemini. While trying to reach feature parity with roam-UI, i ended up with a much larger codebase and a different library i.e used FA2 from graphology and sigma for rendering.
A python script gen_graph.py provides the necessary meta information about node and edge information in a json format. To further improve usability, the code related to ForceAtlas2 layout and Louvain community detection was moved from javascript/browser to this python script.
The entire graph rendering is done by graph.js using the json data referred above.
Mobile view
Except the graph page, the entire site is available in mobile view. Mobile view uses javascript mobile-menu.js to provide navigation.
Layout
The Hugo layout strictly adheres to the new template structure implemented in v0.146.0.
prabu@homepc2 /d/m/p/o/my_hugo_site (master)> tree layouts/
layouts/
├── _default
│ ├── _markup
│ │ └── render-link.html
│ ├── graph-map.html
│ ├── index.json
│ └── single.html
├── _partials
│ ├── date.html
│ ├── footer.html
│ ├── graph_scripts.html
│ ├── header.html
│ ├── meta.html
│ └── neighbour-panel.html
├── baseof.html
├── docs
│ ├── list.html
│ └── single.html
├── journal
│ ├── list.html
│ └── single.html
├── list.html
└── shortcodes
├── call_footer.html
└── recent_notes.html
Earlier Implementations
This section is meant to capture historical implementation details.
Initially used dropbox as cloud storage with org/Resources folder as base for the Zettelkasten system.
Dropbox_uploader script managed synchronization between desktop computers and Dropbox manually. I decided against using Dropbox desktop client, so that the process is as lightweight as possible.
I used dropbox so that the files are available for use with android apps like orgro and orgzly . After testing both, started using orgzly on android mobile Note6Pro to sync with Dropbox.
As on 2024-03-18, instead of using Dropbox , decided to use webdav from rclone to sync with orgzly . This allowed the content on computers to sync with orgzly . Back links were still not supported in orgzly.
On 2024-03-19, checked out another tool named Markor . This tool is not as polished as orgzly, but it is more effective as it supports more file formats. Still backlinks are not working. Totally removed the tool on 2024-03-20.
Decided to exporting the content as HTML with back links is the only solution. Installed rcx on 2024-03-19. Found it easy to sync the org files using webdav between computers and Note6Pro .
Rclone tool in combination with rcx was used until Mar'26 to sync the above HTML content. Rcx also served as web server on the mobile.
First version
My first version of Org Roam was entirely based on systemcrafters article2. This article allowed me to get started quickly using Ananke theme, which i followed up with the official org-roam configuration guide3. “org-id-get-create” shortcut was added from reading this4.
Later i removed the Ananke theme and went for a lightweight site which was usable in both mobile and desktop with simple navigation. I kept a single Javscript based search with hugo as the only dependency.
Git log of this implementation can be found here↗ .
Second version
Around early Feb'26, I read a blog post titled “Visualizing Hugo blog posts with d3js” by Aris Bezas aka igoumeninja↗ . I implemented a new hugo site with hugo-book theme and modified the Javascript provided by the author with generous help from claude and gemini to make it work with my theme and content.
The site supported all modern web features like dark mode support and mobile first UI. I tried to maintain the site lightweight by moving the javascript based features to load only when specifically opted by the user.
Around mid Mar'26, replaced the d3js library by sigma + forceatlas2 from graphology libraries with assistance from gemini. The decision to move was made keeping in mind the scalability of the new library and better graph quality.
After multiple iterations, forceatlas2 was replaced by FA2Layout async so as to benefit from the non-blocking UI. The site navigation was changed to show recent posts and featured posts. Graph and associated javascript still remained optional and available when opted by the website user. The earlier javascript based search was not implemented in this site. Graph view is disabled in mobile UI, as it is not very usable.
Git log of this implementation can be found here↗ .
References
- https://fortelabs.com/blog/masters-of-creative-note-taking-luhmann-and-da-vinci/↗
- https://zenkit.com/en/blog/a-beginners-guide-to-the-zettelkasten-method/↗
- https://zettelkasten.de/posts/overview/↗
- https://blog.viktomas.com/posts/slip-box/↗
- https://blog.viktomas.com/posts/slip-box-after-a-year/↗
- https://www.lesswrong.com/posts/NfdHG6oHBJ8Qxc26s/the-zettelkasten-method-1↗
- https://medium.com/@rebecca9941/the-zettelkasten-method-examples-to-help-you-get-started-8f8a44fa9ae6↗
- https://leananki.com/zettelkasten-method-smart-notes/↗
© Prabu Anand K 2020-2026