diff --git a/docs/example.typ b/docs/example.typ
new file mode 100644
index 0000000..4b614ba
--- /dev/null
+++ b/docs/example.typ
@@ -0,0 +1,39 @@
+#import "../src/lib.typ" as chronos
+
+#let example-preamble = "import \"../src/lib.typ\": *;"
+#let example-scope = (
+  chronos: chronos
+)
+
+#let example(src, show-src: true, vertical: false, fill: true, wrap: true) = {
+  src = src.text
+  let full-src = example-preamble + src
+  let body = eval(full-src, scope: example-scope)
+  let img = if wrap { chronos.diagram(body) } else { body }
+
+  block(width: 100%,
+    align(center,
+      box(
+        stroke: black + 1pt,
+        radius: .5em,
+        fill: if fill {color.white.darken(5%)} else {none},
+        if show-src {
+          let src-block = align(left, raw(src, lang: "typc"))
+          table(
+            columns: if vertical {1} else {2},
+            inset: 1em,
+            align: horizon + center,
+            stroke: none,
+            img,
+            if vertical {table.hline()} else {table.vline()}, src-block
+          )
+        } else {
+          table(
+            inset: 1em,
+            img
+          )
+        }
+      )
+    )
+  )
+}
\ No newline at end of file
diff --git a/docs/examples.typ b/docs/examples.typ
new file mode 100644
index 0000000..3c98aec
--- /dev/null
+++ b/docs/examples.typ
@@ -0,0 +1,116 @@
+#import "example.typ": example
+
+#let seq-comm-align = example(```
+_par("p1", display-name: "Start participant")
+_par("p2", display-name: "End participant")
+let alignments = (
+  "start", "end",
+  "left", "right",
+  "center"
+)
+for a in alignments {
+  _seq(
+    "p2", "p1",
+    comment: raw(a),
+    comment-align: a
+  )
+}
+```)
+
+#let seq-tips = example(```
+let _seq = _seq.with(comment-align: "center")
+_par("a", display-name: "Alice")
+_par("b", display-name: "Bob")
+
+_seq("a", "b", comment: "Various tips", end-tip: "")
+_seq("a", "b", end-tip: ">", comment: `->`)
+_seq("a", "b", end-tip: ">>", comment: `->>`)
+_seq("a", "b", end-tip: "\\", comment: `-\`)
+_seq("a", "b", end-tip: "\\\\", comment: `-\\`)
+_seq("a", "b", end-tip: "/", comment: `-/`)
+_seq("a", "b", end-tip: "//", comment: `-//`)
+_seq("a", "b", end-tip: "x", comment: `->x`)
+_seq("a", "b", start-tip: "x", comment: `x->`)
+_seq("a", "b", start-tip: "o", comment: `o->`)
+_seq("a", "b", end-tip: ("o", ">"), comment: `->o`)
+_seq("a", "b", start-tip: "o",
+               end-tip: ("o", ">"), comment: `o->o`)
+_seq("a", "b", start-tip: ">",
+               end-tip: ">", comment: `<->`)
+_seq("a", "b", start-tip: ("o", ">"),
+               end-tip: ("o", ">"), comment: `o<->o`)
+_seq("a", "b", start-tip: "x",
+               end-tip: "x", comment: `x<->x`)
+_seq("a", "b", end-tip: ("o", ">>"), comment: `->>o`)
+_seq("a", "b", end-tip: ("o", "\\"), comment: `-\o`)
+_seq("a", "b", end-tip: ("o", "\\\\"), comment: `-\\o`)
+_seq("a", "b", end-tip: ("o", "/"), comment: `-/o`)
+_seq("a", "b", end-tip: ("o", "//"), comment: `-//o`)
+_seq("a", "b", start-tip: "x",
+               end-tip: ("o", ">"), comment: `x->o`)
+```)
+
+#let grp = example(```
+_par("a", display-name: "Alice")
+_par("b", display-name: "Bob")
+
+_grp("Group 1", desc: "Description", {
+  _seq("a", "b", comment: "Authentication")
+  _grp("loop", desc: "1000 times", {
+    _seq("a", "b", comment: "DoS Attack")
+  })
+  _seq("a", "b", end-tip: "x")
+})
+```)
+
+#let sync = example(```
+_par("alice", display-name: "Alice")
+_par("bob", display-name: "Bob")
+_par("craig", display-name: "Craig")
+
+_seq("bob", "alice")  // Unsynchronized
+_seq("bob", "craig")  //  "
+_sync({
+  _seq("bob", "alice")  // Synchronized
+  _seq("bob", "craig")  //  "
+})
+_seq("alice", "bob")  // Unsynchronized
+_seq("craig", "bob")  //  "
+_sync({
+  _seq("alice", "bob")  // Synchronized
+  _seq("craig", "bob")  //  "
+})
+```)
+
+#let gaps-seps = example(```
+_par("alice", display-name: "Alice")
+_par("bob", display-name: "Bob")
+
+_seq("alice", "bob", comment: "Hello")
+_gap(size: 10)
+_seq("bob", "alice", comment: "Hi")
+_sep("Another day")
+_seq("alice", "bob", comment: "Hello again")
+```)
+
+#let notes-shapes = example(```
+_par("alice", display-name: "Alice")
+_par("bob", display-name: "Bob")
+_note("over", `default`, pos: "alice")
+_note("over", `rect`, pos: "bob", shape: "rect")
+_note("over", `hex`, pos: ("alice", "bob"), shape: "hex")
+```)
+
+#let notes-sides = example(```
+_par("alice", display-name: "Alice")
+_par("bob", display-name: "Bob")
+_par("charlie", display-name: "Charlie")
+_note("left", [`left` of Alice], pos: "alice")
+_note("right", [`right` of Charlie], pos: "charlie")
+_note("over", [`over` Alice and Bob], pos: ("alice", "bob"))
+_note("across", [`across` all participants])
+_seq("alice", "bob")
+_note("left", [linked with sequence])
+_note("over", [A note], pos: "alice")
+_note("over", [Aligned note], pos: "charlie", aligned: true)
+```, vertical: true)
\ No newline at end of file
diff --git a/docs/gaps_seps.typ b/docs/gaps_seps.typ
new file mode 100644
index 0000000..d569ab9
--- /dev/null
+++ b/docs/gaps_seps.typ
@@ -0,0 +1,8 @@
+/// Creates a gap before the next element
+/// - size (int): Size of the gap
+#let _gap(size: 20) = {}
+
+/// Creates a separator before the next element
+/// #examples.gaps-seps
+/// - name (content): Name to display in the middle of the separator
+#let _sep(name) = {}
\ No newline at end of file
diff --git a/docs/groups.typ b/docs/groups.typ
new file mode 100644
index 0000000..76ed0f7
--- /dev/null
+++ b/docs/groups.typ
@@ -0,0 +1,19 @@
+/// Creates a group of sequences
+/// #examples.grp
+/// - name (content): The group's name
+/// - desc (none, content): Optional description
+/// - type (str): The groups's type (unused for the moment)
+/// - elmts (array): Elements inside the group (can be sequences, other groups, notes, etc.)
+#let _grp(
+  name,
+  desc: none,
+  type: "default",
+  elmts
+) = {}
+
+/// Synchronizes multiple sequences
+/// #examples.sync
+/// - elmts (array): Synchronized elements (generally sequences or notes)
+#let _sync(
+  elmts
+)
\ No newline at end of file
diff --git a/docs/notes.typ b/docs/notes.typ
new file mode 100644
index 0000000..3b783eb
--- /dev/null
+++ b/docs/notes.typ
@@ -0,0 +1,23 @@
+/// Creates a note
+/// - side (str): The side on which to place the note (see @@SIDES for accepted values)
+/// - content (content): The note's content
+/// - pos (none, str, array): Optional participant(s) on which to draw next to / over. If `side` is "left" or "right", sets next to which participant the note is placed. If `side` is "over", sets over which participant(s) it is placed
+/// - color (color): The note's color
+/// - shape (str): The note's shape (see @@SHAPES for accepted values)
+/// - aligned (bool): True if the note is aligned with another note, in which case `side` must be `"over"`, false otherwise
+#let _note(
+  side,
+  content,
+  pos: none,
+  color: rgb("#FEFFDD"),
+  shape: "default",
+  aligned: false
+) = {}
+
+/// Accepted values for `shape` argument of @@_note()
+/// #examples.notes-shapes
+#let SHAPES = ("default", "rect", "hex")
+
+/// Accepted values for `side` argument of @@_note()
+/// #examples.notes-sides
+#let SIDES = ("left", "right", "over", "across")
\ No newline at end of file
diff --git a/docs/participants.typ b/docs/participants.typ
new file mode 100644
index 0000000..d6cd62c
--- /dev/null
+++ b/docs/participants.typ
@@ -0,0 +1,51 @@
+/// Possible participant shapes
+/// #box(width: 100%, align(center)[
+///   #chronos.diagram({
+///     import chronos: *
+///     let _par = _par.with(show-bottom: false)
+///     _par("Foo", display-name: "participant", shape: "participant")
+///     _par("Foo1", display-name: "actor", shape: "actor")
+///     _par("Foo2", display-name: "boundary", shape: "boundary")
+///     _par("Foo3", display-name: "control", shape: "control")
+///     _par("Foo4", display-name: "entity", shape: "entity")
+///     _par("Foo5", display-name: "database", shape: "database")
+///     _par("Foo6", display-name: "collections", shape: "collections")
+///     _par("Foo7", display-name: "queue", shape: "queue")
+///     _par("Foo8", display-name: "custom", shape: "custom", custom-image: TYPST)
+///     _gap()
+///   })
+/// ])
+#let SHAPES = (
+  "participant",
+  "actor",
+  "boundary",
+  "control",
+  "entity",
+  "database",
+  "collections",
+  "queue",
+  "custom"
+)
+
+/// Creates a new participant
+/// - name (str): Unique participant name used as reference in other functions
+/// - display-name (auto, content): Name to display in the diagram. If set to `auto`, `name` is used
+/// - from-start (bool): If set to true, the participant is created at the top of the diagram. Otherwise, it is created at the first reference
+/// - invisible (bool): If set to true, the participant will not be shown
+/// - shape (str): The shape of the participant. Possible values in @@SHAPES
+/// - color (color): The participant's color
+/// - custom-image (none, image): If shape is 'custom', sets the custom image to display
+/// - show-bottom (bool): Whether to display the bottom shape
+/// - show-top (bool): Whether to display the top shape
+/// -> array
+#let _par(
+  name,
+  display-name: auto,
+  from-start: true,
+  invisible: false,
+  shape: "participant",
+  color: rgb("#E2E2F0"),
+  custom-image: none,
+  show-bottom: true,
+  show-top: true,
+) = {}
\ No newline at end of file
diff --git a/docs/sequences.typ b/docs/sequences.typ
new file mode 100644
index 0000000..e9be9a6
--- /dev/null
+++ b/docs/sequences.typ
@@ -0,0 +1,60 @@
+/// Manually adds an event to the given participant
+/// - participant (str): The participant concerned by the event
+/// - event (str): The event type (see @@EVENTS for ccepted values)
+#let _evt(participant, event) = {}
+
+/// Creates a sequence / message between two participants
+/// - p1 (str): Start participant
+/// - p2 (str): End participant
+/// - comment (none, content): Optional comment to display along the arrow
+/// - comment-align (str): Where to align the comment with respect to the arrow (see @@comment-align for accepted values)
+/// - dashed (bool): Whether the arrow's stroke is dashed or not
+/// - start-tip (str): Start arrow tip (see @@tips for accepted values)
+/// - end-tip (str): End arrow tip (see @@tips for accepted values)
+/// - color (color): Arrow's color
+/// - flip (bool): If true, the arrow is flipped (goes from end to start). This is particularly useful for self calls, to change the side on which the arrow appears
+/// - enable-dst (bool): If true, enables the destination lifeline
+/// - create-dst (bool): If true, creates the destination lifeline and participant
+/// - disable-dst (bool): If true, disables the destination lifeline
+/// - destroy-dst (bool): If true, destroys the destination lifeline and participant
+/// - disable-src (bool): If true, disables the source lifeline
+/// - destroy-src (bool): If true, destroy the source lifeline and participant
+/// - lifeline-style (auto, dict): Optional styling options for lifeline rectangles (see CeTZ documentation for more information on all possible values)
+/// - slant (none, int): Optional slant of the arrow
+/// -> array
+#let _seq(
+  p1,
+  p2,
+  comment: none,
+  comment-align: "left",
+  dashed: false,
+  start-tip: "",
+  end-tip: ">",
+  color: black,
+  flip: false,
+  enable-dst: false,
+  create-dst: false,
+  disable-dst: false,
+  destroy-dst: false,
+  disable-src: false,
+  destroy-src: false,
+  lifeline-style: auto,
+  slant: none
+) = {}
+
+/// Accepted values for `event` argument of @@_evt()
+/// 
+/// `EVENTS = ("create", "destroy", "enable", "disable")`
+#let EVENTS = ("create", "destroy", "enable", "disable")
+
+/// Accepted values for `start-tip` and `end-tip` arguments of @@_seq()
+/// #examples.seq-tips
+#let tips = (
+  "", ">", ">>", "\\", "\\\\", "/", "//", "x", "o",
+)
+
+/// Accepted values for `comment-align` argument of @@_seq()
+/// #examples.seq-comm-align
+#let comment-align = (
+  "start", "end", "left", "center", "right"
+)
\ No newline at end of file
diff --git a/manual.pdf b/manual.pdf
new file mode 100644
index 0000000..cc687c0
Binary files /dev/null and b/manual.pdf differ
diff --git a/manual.typ b/manual.typ
new file mode 100644
index 0000000..bc888a1
--- /dev/null
+++ b/manual.typ
@@ -0,0 +1,214 @@
+#import "@preview/tidy:0.3.0"
+#import "src/lib.typ" as chronos
+#import "src/participant.typ" as mod-par
+#import "docs/examples.typ"
+#import "docs/example.typ": example
+
+#let TYPST = image("gallery/typst.png", width: 1.5cm, height: 1.5cm, fit: "contain")
+
+#let doc-ref(target, full: false, var: false) = {
+  let (module, func) = target.split(".")
+  let label-name = module + func
+  let display-name = func
+  if full {
+    display-name = target
+  }
+  if not var {
+    label-name += "()"
+    display-name += "()"
+  }
+  link(label(label-name))[#display-name]
+}
+
+#set heading(numbering: (..num) => if num.pos().len() < 4 {
+  numbering("1.1", ..num)
+})
+#{
+  outline(indent: true, depth: 3)
+}
+#show link: set text(fill: blue)
+
+#set page(numbering: "1/1", header: align(right)[chronos #sym.dash.em v#chronos.version])
+#set page(
+  header: locate(loc => align(left)[chronos #sym.dash.em v#chronos.version]),
+  footer: locate(loc => align(center, counter(page).display("1/1", both: true)))
+)
+
+= Introduction
+
+This package lets you create nice sequence diagrams using the CeTZ package.
+
+= Usage
+
+Simply import #link("https://typst.app/universe/package/chronos/")[chronos] and call the `diagram` function:
+#pad(left: 1em)[```typ
+#import "@preview/chronos:0.1.0"
+#chronos.diagram({
+  import chronos: *
+  ...
+})
+```]
+
+= Examples
+
+You can find the following examples and more in the #link("https://git.kb28.ch/HEL/circuiteria/src/branch/main/gallery")[gallery] directory
+
+== Some groups and sequences
+
+#example(```
+chronos.diagram({
+  import chronos: *
+  _seq("Alice", "Bob", comment: "Authentication Request")
+  _seq("Bob", "Alice", comment: "Authentication Failure")
+
+  _grp("My own label", desc: "My own label2", {
+    _seq("Alice", "Log", comment: "Log attack start")
+    _grp("loop", desc: "1000 times", {
+      _seq("Alice", "Bob", comment: "DNS Attack")
+    })
+    _seq("Alice", "Bob", comment: "Log attack end")
+  })
+})
+```, wrap: false, vertical: true)
+
+#pagebreak(weak: true)
+
+== Lifelines
+
+#example(```
+chronos.diagram({
+  import chronos: *
+  _seq("alice", "bob", comment: "hello", enable-dst: true)
+  _seq("bob", "bob", comment: "self call", enable-dst: true)
+  _seq(
+    "bill", "bob",
+    comment: "hello from thread 2",
+    enable-dst: true,
+    lifeline-style: (fill: rgb("#005500"))
+  )
+  _seq("bob", "george", comment: "create", create-dst: true)
+  _seq(
+    "bob", "bill",
+    comment: "done in thread 2",
+    disable-src: true,
+    dashed: true
+  )
+  _seq("bob", "bob", comment: "rc", disable-src: true, dashed: true)
+  _seq("bob", "george", comment: "delete", destroy-dst: true)
+  _seq("bob", "alice", comment: "success", disable-src: true, dashed: true)
+})
+```, wrap: false, vertical: true)
+
+#pagebreak(weak: true)
+
+== Found and lost messages
+
+#example(```
+chronos.diagram({
+  import chronos: *
+  _seq("?", "Alice", comment: [?->\ *short* to actor1])
+  _seq("[", "Alice", comment: [\[->\ *from start* to actor1])
+  _seq("[", "Bob", comment: [\[->\ *from start* to actor2])
+  _seq("?", "Bob", comment: [?->\ *short* to actor2])
+  _seq("Alice", "]", comment: [->\]\ from actor1 *to end*])
+  _seq("Alice", "?", comment: [->?\ *short* from actor1])
+  _seq("Alice", "Bob", comment: [->\ from actor1 to actor2])
+})
+```, wrap: false, vertical: true)
+
+#pagebreak(weak: true)
+
+== Custom images
+
+#example(```
+let load-img(path) = image(path, width: 1.5cm, height: 1.5cm, fit:"contain")
+let TYPST = load-img("../gallery/typst.png")
+let FERRIS = load-img("../gallery/ferris.png")
+let ME = load-img("../gallery/me.jpg")
+
+chronos.diagram({
+  import chronos: *
+  _par("me", display-name: "Me", shape: "custom", custom-image: ME)
+  _par("typst", display-name: "Typst", shape: "custom", custom-image: TYPST)
+  _par("rust", display-name: "Rust", shape: "custom", custom-image: FERRIS)
+
+  _seq("me", "typst", comment: "opens document", enable-dst: true)
+  _seq("me", "typst", comment: "types document")
+  _seq("typst", "rust", comment: "compiles content", enable-dst: true)
+  _seq("rust", "typst", comment: "renders document", disable-src: true)
+  _seq("typst", "me", comment: "displays document", disable-src: true)
+})
+```, wrap: false, vertical: true)
+
+#pagebreak(weak: true)
+
+= Reference
+
+#let par-docs = tidy.parse-module(
+  read("docs/participants.typ"),
+  name: "Participants",
+  require-all-parameters: true,
+  scope: (
+    chronos: chronos,
+    mod-par: mod-par,
+    TYPST: TYPST,
+    doc-ref: doc-ref
+  )
+)
+#tidy.show-module(par-docs, show-outline: false)
+
+#pagebreak(weak: true)
+
+#let seq-docs = tidy.parse-module(
+  read("docs/sequences.typ"),
+  name: "Sequences",
+  require-all-parameters: true,
+  scope: (
+    chronos: chronos,
+    doc-ref: doc-ref,
+    examples: examples
+  )
+)
+#tidy.show-module(seq-docs, show-outline: false, sort-functions: none)
+
+#pagebreak(weak: true)
+
+#let grp-docs = tidy.parse-module(
+  read("docs/groups.typ"),
+  name: "Groups",
+  require-all-parameters: true,
+  scope: (
+    chronos: chronos,
+    doc-ref: doc-ref,
+    examples: examples
+  )
+)
+#tidy.show-module(grp-docs, show-outline: false)
+
+#pagebreak(weak: true)
+
+#let gap-sep-docs = tidy.parse-module(
+  read("docs/gaps_seps.typ"),
+  name: "Gaps and separators",
+  require-all-parameters: true,
+  scope: (
+    chronos: chronos,
+    doc-ref: doc-ref,
+    examples: examples
+  )
+)
+#tidy.show-module(gap-sep-docs, show-outline: false)
+
+#pagebreak(weak: true)
+
+#let notes-docs = tidy.parse-module(
+  read("docs/notes.typ"),
+  name: "Notes",
+  require-all-parameters: true,
+  scope: (
+    chronos: chronos,
+    doc-ref: doc-ref,
+    examples: examples
+  )
+)
+#tidy.show-module(notes-docs, show-outline: false)
diff --git a/src/lib.typ b/src/lib.typ
index ec5d651..3e7383e 100644
--- a/src/lib.typ
+++ b/src/lib.typ
@@ -1,3 +1,4 @@
+#let version = version(0, 1, 0)
 #import "diagram.typ": diagram, from-plantuml, _gap, _evt
 
 #import "sequence.typ": _seq