Introduction to Luhmann's Zettelkasten

Niklas Luhmann’s Zettelkasten is becoming increasingly popular for being a great note taking technique. However, it is often misunderstood as taking notes without any structure, whereas Luhmann actually structured his notes hierarchically, but also allowed for arbitrary links between notes. This post will describe the general note-taking workflow that Luhmann used and a practical implementation of the Zettelkasten in Emacs’ built-in org-mode, which I have been using regularly for my notes and has been working well.

** Zettelkasten compared to other note-taking solutions

I have gone through many different phases of note taking, from trying to keep handwritten notes in a folder, to using digital notes organised by topic, or notes organised by tags. None of these methods really worked for me though, either I would not be able to find the relevant notes anymore, or I wouldn’t really be able to figure out the best place to put a note.

There are various existing note taking solutions that people have come up with other the years, so why care about the technique that Luhmann used? What intrigued me the most about this technique is that it combined what I liked about structuring my notes in a linear and hierarchical fashion, with links to other notes which added an element of unstructured notes. This way, notes can just be written in any linear fashion one chooses, and link to any other note if the topics are similar enough. In addition to that, even though notes are written in a linear fashion, one can always add new notes in between any other notes, meaning one does not have to worry too much about writing notes one after another that are not too related. And whenever one wants to discuss a topic, it can always be branched off into a new sequence of notes.

This method is far more flexible than other note-taking methods I used before, but also less stressful as it is designed in a way that the actual organisation of the notes does not matter too much in the end. In addition to that, if time is set aside to try and link notes together once in a while, or when a new note is inserted, then it should be straightforward to try and find relevant notes again. Even though finding a specific note again isn’t that easy, as they may be nested deeply in a structure that may not be directly related to the subject, if these are linked back to other notes of the same topic it should be possible to explore these eventually when the notes are looked at again at some point in the future. After some time, it may also be necessary to build manual indices for sub topics in the notes which links to a few notes that relate to the topic.

** General setup

When writing notes using this method, you do not have to think too much about where the note should go. However, the first thing to check is whether there is a note that may already be similar enough to the note you want to insert. An example setup could be the following, which is separated into three topics: (1) high-level synthesis (HLS), (2) computing and (3) verification.

Figure 1: The first step is to create a tree of notes in any hierarchy that seems to suit it best. Because of the numbering of the notes, other notes can always be inserted in between notes by adding a letter or number to the end.

Figure 1: The first step is to create a tree of notes in any hierarchy that seems to suit it best. Because of the numbering of the notes, other notes can always be inserted in between notes by adding a letter or number to the end.

We can then add notes as shown above, adding them into the right category under a heading that makes the most sense, thereby building a tree. Each note is assigned a unique identifier (ID), which it can then be referred to later. These identifiers contain either numbers or letters, where sequential notes just increment the last number or letter in the ID. Then, if a note needs to be added to a note that already has a successor, a branch can be created by taking the ID and adding a 1 or an a at the end of it to create the new ID.

After having a tree like that, the idea is that notes can be interconnected in any way that seems fit, if the contents of the note are generally relevant to the other note. This results in the diagram below that still has the general structure of the tree but also contains interconnections to other notes that might be in a completely different topic but might still be generally relevant.

Figure 2: Once in a while, links to other notes in other categories or in the same category should be made.

Figure 2: Once in a while, links to other notes in other categories or in the same category should be made.

This allows for notes to be written anywhere that makes sense, but still connect to other areas in the notes that also might be relevant, thereby creating a network of relevant notes that might connect these topics in different ways. This means that it is easy to jump around from topic to topic by following relevant notes around, adding more links if these come up and making them permanent by adding them to the right notes. The hope is that this eventually leads to a second brain where all the links between topics and notes are permanently there. This leads to it being possible to browse the brain explicitly and observe the connections that were made that may have been forgotten and therefore lead to new discoveries.

** Inserting new notes

There are several possible notes that can be inserted into the Zettelkasten, but the need for them should arise naturally and one therefore doesn’t have to think about the separate types of notes directly. In addition to the following types of notes, Luhmann also had a separate box for references and notes about those references, however, these are not added to the Zettelkasten in my case because I felt like using tools specifically to keep track of references is a better system for me. This is mentioned further in the keeping track of references section.

*** Permanent notes

Inserting new notes into the Zettelkasten can be done for any new piece of information one wants to permanently add to the tree of notes and therefore the network of notes. These are therefore called “permanent notes,” however, these are not the only notes that may appear in the network. The most important thing to take into consideration is that “permanent notes” should be completely in your own words, and express an idea that also links to other parts in the network. At the start it may be necessary to create a few topics that these notes fit into, however, eventually one should be able to find notes that are similar enough which this new note should follow.

*** Index notes

Apart from that, there can also be “index notes,” which try to give some structure to a subsection that may have gotten lost with all of the branches that may have been added. In addition to that, these may tie in other notes from other topics as well that relate to that topic. These can therefore just be added whenever you feel like there are too many notes for a subtopic and cannot keep track of all the possible links.

** Keeping track of references

Luhmann kept track of references by inserting them into their own box in a linear structure and then referring to them by ID whenever they needed to be cited. These are often called “bibliographical notes.” In addition to that, notes that were not permanent and more relevant to a specific paper or book were also added separately to the other notes and were called “literature notes,” as these often contained summaries of the papers or books that were cited. Even though these were written in his own words, they only really were relevant to the paper itself as temporary notes, which could eventually be added as “permanent notes” into the Zettelkasten and linked to other notes when a narrative developed that did link this piece of knowledge to other notes.

As references are quite separate to the other notes anyways, I prefer to keep them quite separate as well, and instead use standard bibliography management tools to keep track of all my references as well as linking notes to the references in the tool itself. In my case this is using ebib in Emacs, however, any alternative works as well, such as Zotero.

In my notes, I then reference these by their bibtex identifier that is automatically generated, and which is later used when referencing the same literature in LaTeX, for example. This allows me to keep these notes quite separate and forces me to think about links when I do eventually add them to the network as “permanent notes.”

** Emacs implementation

If anything touches plain text, then it is possible to efficiently implement it using org-mode in Emacs. org-mode already has most of the functionality that is needed for taking notes in this way, we just have to show the layout that I am currently using for note taking.

org-mode is a plain text file format that can be used from simple note taking to a complete task tracker with an agenda and organisation system. It can easily be adapted to implement the note taking described in this post. The first step to create the notes directory is just to think about a note one would want to write, and what a general topic is that it could fit into. A file for that topic can then be created, for example, my topics are the following:

  • hls.org: my notes on high-level synthesis,
  • verification.org: my notes on verification, and
  • computing.org: my notes on computer science topics.

A screenshot of how the top-level view of all my files looks like is shown in the screenshot below.

Figure 3: Example of the files containing the three topics I take notes in, displayed using the columns view in org-mode.

Figure 3: Example of the files containing the three topics I take notes in, displayed using the columns view in org-mode.

Next, we can keep adding notes to the respective files, and whenever we can see a possible link between two notes, we can add that to the relevant note. However, once in a while we have to take time to go through a lot of the notes and try to make conscious links between other topics and add the relevant links.

*** Linking to other notes

The main feature that is needed is linking to other notes and assigning IDs to existing notes so that these can be referenced. In org-mode, this can be done using the CUSTOM_ID property, which can be set for every header and then linked to easily using a simple org-mode link. The only problem is that the CUSTOM_ID then needs to be created manually from the previous one, however, this can be automated as shown in the automatic ID creation section.

*** Some automation for ID creation

Finally, to conclude we can also add some automation to creating new notes. The first function we’ll need is one which generates an ID for us. We’ll need two functions, one which increments the current ID, and one which will branch off of the current ID and create a parallel one. We can therefore first create a simple ymhg/incr-id function which simply increments the last character of the ID.

(defun ymhg/incr-id (ident)
  (let ((ident-list (string-to-list ident)))
    (cl-incf (car (last ident-list)))
    (concat ident-list)))

However, one problem is that if we get to an ID with a 9, it will stop incrementing correctly and turn into a :,1 which will break the naming scheme. This could simply be fixed by turning the last value of the ID into a number, incrementing that, then turning it back into the original representation. However, for the current purpose we’ll just assume that manual intervention will be required sometimes. Then, to create the function that generates an ID branching off of the current one, we just have to check if the current id ends with a number or a character, and add a a or 1 accordingly.

(defun ymhg/branch-id (ident)
  (if (string-match-p ".*[0-9]$" ident)
      (concat ident "a")
    (concat ident "1")))

Finally, we just need functions that create a new headline underneath the current one, with the correct level and the correct ID. To do this, we first need two functions, one which creates a new function that gets the ID of the current heading, generate the new heading, and then insert a new heading and generate the new ID for that heading. We can then write a similar function that instead generates a branching ID and creates a subheading compared to the same-level heading. However, as these functions are extremely similar, the only differences being what heading to add and how to increment the ID, we can create a general function that will get an ID, increment it and then generate a new heading somehow using that ID.

(defun ymhg/org-zettelkasten-create (incr newheading)
  (let* ((current-id (org-entry-get nil "CUSTOM_ID"))
	 (next-id (funcall incr current-id)))
    (funcall newheading)
    (org-set-property "CUSTOM_ID" next-id)))

Using that general function, we can then first create the function that will insert a heading at the same level as the previous heading and increments the last value of the ID.

(defun org-zettelkasten-create-heading ()
  (ymhg/org-zettelkasten-create
   'ymhg/incr-id 'org-insert-heading))

Then we create the function that will increment the ID by adding an a or 1 after the ID, and inserts a sub-heading.

(defun org-zettelkasten-create-subheading ()
  (ymhg/org-zettelkasten-create
   'ymhg/branch-id '(lambda () (org-insert-subheading ""))))

For the final part of automation, we can then create a function that will correctly use the create-next and create-branch function depending on the current location of the cursor. To see which function should be used. The main idea behind this function is that we first go back to the current heading using org-back-to-heading and then try and go forward to the next heading which is at the same level using org-forward-heading-same-level. If these are at different locations, then we know that there is a next heading on the same level, which means that we need to branch off of the current one. If we are still in the same location, then we can create a new note at the same level.

(defun org-zettelkasten-create-dwim ()
  (interactive)
  (let ((current-point (save-excursion
			 (org-back-to-heading)
			 (point)))
	(next-point (save-excursion
		      (org-forward-heading-same-level 1 t)
		      (point))))
    (if (= current-point next-point)
	(org-zettelkasten-create-heading)
	(org-zettelkasten-create-subheading))))

Finally, we can then add the create-dwim function to our keybinding map and we’re ready to create as many notes as possible.

(define-key org-mode-map (kbd "C-c y n") #'org-zettelkasten-create-dwim)

** Conclusion

To conclude, there are currently many approaches that try to mimic Luhmann’s Zettelkasten, however, I don’t believe that many actually follow his philosophy that closely, and therefore lose on some of the benefits that this note-taking technique provides. I therefore preferred implementing it as simply as possible and leveraging the powerful org-mode to get a system that works for me.

I do not pretend to have the perfect implementation of a digital Zettelkasten, and there are many features that are still missing, such as a keyword index, which Luhmann used as an alternative index into the Zettelkasten. However, for now I haven’t had a need for that yet, and therefore have not thought about how to best implement it. It could always be implemented manually like Luhmann did, but it could also be implemented automatically by using tags, for example. In addition to that, there are other note-taking tools, especially roam research-like tools, which provide a lot of functionally for unordered notes, such as seeing a directed acyclic graph (DAG) view of your notes and provide back-links to other notes that refer to this note.

I hope this post helps when maybe choosing an existing implementation, of which there are plenty, and to see if these have all the features that you need or want from a note-taking tool. If that is not the case, I hope that I have also convinced you that creating your own does not have to be painful and that it really does not inherently need many features. I will also be following this up with a post on how to use the Zettelkasten to write blog posts or papers and organising them using these notes.


  1. Thanks to Joseph Turner for refactoring the ymhg/incr-id function and correcting the description of incrementing past 9. ↩︎