diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ce572b41ddd1758dd9b9ef852ab8dfd6e9c4572a
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,53 @@
+image: ubuntu:latest
+
+stages:
+  - code_embed
+  - ruff
+
+variables:
+  GIT_STRATEGY: clone
+
+code_embedder:
+  stage: code_embed
+  script:
+    - cd code-embedder/
+    - poetry install --only main
+    - README_PATHS=$(find .. -maxdepth 1 -name "*.md" -print)
+    - poetry run python ./src/main.py --readme-paths $README_PATHS
+    - cd ..
+    - README_PATHS=$(find . -maxdepth 1 -name "*.md" -print)
+    - git add $README_PATHS
+    - git diff --staged
+    - export BRANCH_NAME="${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME:-$CI_COMMIT_REF_NAME}"
+    - echo "branch name $BRANCH_NAME"
+    - git remote set-url origin "https://oauth2:${PERSONAL_ACCESS_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git"
+    - echo "https://oauth2:${PERSONAL_ACCESS_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git"
+    - if ! git diff --cached --exit-code; then
+        git commit -m "Apply Code Embedder";
+        git push origin HEAD:$BRANCH_NAME;
+      else
+        echo "No changes to commit";
+      fi
+
+  before_script:
+    - apt-get update && apt-get install -y curl git python3 python3-pip
+    - curl -sSL https://install.python-poetry.org | python3 -
+    - export PATH="$HOME/.local/bin:$PATH"
+    - git clone https://github.com/kvankova/code-embedder.git --branch "v0.5.2"
+    - git config --global user.email $GITLAB_USER_EMAIL
+    - git config --global user.name $GITLAB_USER_NAME
+  only:
+    - merge_requests
+    
+
+ruff_linter:
+  stage: ruff
+  script: 
+    - curl -LsSf https://astral.sh/uv/install.sh | sh
+    - source $HOME/.local/bin/env
+    - uv tool run ruff check
+  before_script:
+    - apt-get update && apt-get install -y curl
+  only:
+    - merge_requests
+
diff --git a/README.md b/README.md
index 8fd4e0c2241bd668110e537d52f9462a7f68b0d8..ab4ddea5a110668e1599b8da12b1b555b7cbba8c 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,128 @@
 # Json Datadefinition
 ![](./examples/logo.svg)
 
-This repository contains `.h5` I/O utilities for the RoboCT group at Deggendorf. The data structure is auto generated from [THD JSON](https://mygit.th-deg.de/roboct/definitions/json_schemas).
-For information of the data structure please look at `THD JSON`.
+This repository contains `.h5` I/O utilities for the RoboCT group at Deggendorf. The data structure is auto generated from [THD JSON](https://mygit.th-deg.de/roboct-public/roboct-schemas).
+For information of the data structure / group names please look at `THD JSON`.
 
+## Examples
 
+The main groups for a RoboCT project can be seen here [here](./examples/README.md).
+You can explore small dataset with the HDF5 Viewer.
+
+![](./examples/h5_viewer.png)
+
+
+## Usage
+
+### Initialize a File
+
+```python:../scripts/generate_example_projection.py
+from h5py import File
+import numpy as np
+from h5schemas.projection.add_projection import (
+    init_projection_dataset,
+    add_projection_sample,
+)
+
+
+def main():
+    # Generate a file in write mode.
+    test_file = File("./examples/projection.h5", "w")
+
+    # Initialize the .H5 structure
+    init_projection_dataset(test_file, False, (128, 128), (0.139, 0.139))
+
+    # Add dummy projections
+    add_projection_sample(
+        test_file,
+        1e5 * np.random.random((128, 128)),
+        [0.0, 1.0, 2.0],
+        [3.0, 4.0, 5.0, 6.0],
+        [7.0, 8.0, 9.0],
+        [10.0, 11.0, 12.0, 13],
+    )
+    add_projection_sample(
+        test_file,
+        1e5 * np.random.random((128, 128)),
+        np.array([0.0, 1.0, 2.0]),
+        np.array([3.0, 4.0, 5.0, 6.0]),
+        np.array([7.0, 8.0, 9.0]),
+        np.array([10.0, 11.0, 12.0, 13]),
+        voltage_kv=100,
+    )
+
+    # Access data
+    print(test_file["/image/image_array"][...])
+
+
+if __name__ == "__main__":
+    main()
+
+```
+
+### Append new Group
+
+
+```python:../scripts/add_joint_states_to_projection.py
+from h5py import File
+import numpy as np
+from h5schemas.projection.add_projection import (
+    add_projection_sample,
+)
+from h5schemas.thd_joint_states.add_thd_joint_states import (
+    init_thd_joint_states,
+    add_thd_joint_states_sample,
+)
+
+
+def main():
+    # Generate a file in write mode.
+    test_file = File("./examples/projection.h5", "a")
+
+    # Add a new projection sample
+    add_projection_sample(
+        test_file,
+        1e5 * np.random.random((128, 128)),
+        [0.0, 1.0, 2.0],
+        [3.0, 4.0, 5.0, 6.0],
+        [7.0, 8.0, 9.0],
+        [10.0, 11.0, 12.0, 13],
+    )
+
+    # Add a new group
+    init_thd_joint_states(test_file)
+    add_thd_joint_states_sample(test_file, *np.arange(15))
+
+
+if __name__ == "__main__":
+    main()
+
+```
+
+### Slice / Load a Dataset
+
+```python:../scripts/load_slice.py
+from h5py import File
+from h5schemas.load_utilities.projection import extract_projection, GeomOptions
+
+
+def main():
+    # Load the second projection from the dataset
+    test_file = File("./examples/projection.h5", "r")
+    projection = extract_projection(test_file, 1, GeomOptions.PROJECTION_GEOMETRY)
+    print(projection.image_array.shape)
+
+    # Load the first to projections an slice the projection data [20:50, 50:100:2]
+    projection = extract_projection(
+        test_file,
+        slice(0, 2),
+        GeomOptions.PROJECTION_GEOMETRY,
+        crop=(slice(20, 50), slice(50, 100, 2)),
+    )
+    print(projection.image_array.shape)
+
+
+if __name__ == "__main__":
+    main()
+
+```
\ No newline at end of file
diff --git a/dump.txt b/dump.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a0ceb046408602ca75221595cf71ebe0e692c5b3
Binary files /dev/null and b/dump.txt differ
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..0d198b88387dae6b58d232c1177f30dbf732af6a
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,80 @@
+```bash
+.\examples\projection.h5  (5 objects)
+├── detector  (2 objects)
+│   ├── image_dimensions_px  (2 objects)
+│   │   ├── u  (1,), int32
+│   │   └── v  (1,), int32
+│   └── pixel_pitch_mm  (2 objects)
+│       ├── u  (1,), float64
+│       └── v  (1,), float64
+├── image  (2 objects)
+│   ├── header  (2 objects)
+│   │   ├── timestamp  (2,), float64
+│   │   └── uuid  (2,), |S100
+│   └── image_array  (2, 128, 128), uint16
+├── projection_geometry  (6 objects)
+│   ├── detector_center_orientation_quat  (4 objects)
+│   │   ├── w  (2,), float64
+│   │   ├── x  (2,), float64
+│   │   ├── y  (2,), float64
+│   │   └── z  (2,), float64
+│   ├── detector_center_position_mm  (3 objects)
+│   │   ├── x  (2,), float64
+│   │   ├── y  (2,), float64
+│   │   └── z  (2,), float64
+│   ├── detector_center_rotation_matrix  (2 objects)
+│   │   ├── u  (3 objects)
+│   │   │   ├── x  (0,), float64
+│   │   │   ├── y  (0,), float64
+│   │   │   └── z  (0,), float64
+│   │   └── v  (3 objects)
+│   │       ├── x  (0,), float64
+│   │       ├── y  (0,), float64
+│   │       └── z  (0,), float64
+│   ├── focal_spot_orientation_quat  (4 objects)
+│   │   ├── w  (2,), float64
+│   │   ├── x  (2,), float64
+│   │   ├── y  (2,), float64
+│   │   └── z  (2,), float64
+│   ├── focal_spot_position_mm  (3 objects)
+│   │   ├── x  (2,), float64
+│   │   ├── y  (2,), float64
+│   │   └── z  (2,), float64
+│   └── header  (2 objects)
+│       ├── timestamp  (2,), float64
+│       └── uuid  (2,), |S100
+├── projection_geometry_nominal  (6 objects)
+│   ├── detector_center_orientation_quat  (4 objects)
+│   │   ├── w  (2,), float64
+│   │   ├── x  (2,), float64
+│   │   ├── y  (2,), float64
+│   │   └── z  (2,), float64
+│   ├── detector_center_position_mm  (3 objects)
+│   │   ├── x  (2,), float64
+│   │   ├── y  (2,), float64
+│   │   └── z  (2,), float64
+│   ├── detector_center_rotation_matrix  (2 objects)
+│   │   ├── u  (3 objects)
+│   │   │   ├── x  (0,), float64
+│   │   │   ├── y  (0,), float64
+│   │   │   └── z  (0,), float64
+│   │   └── v  (3 objects)
+│   │       ├── x  (0,), float64
+│   │       ├── y  (0,), float64
+│   │       └── z  (0,), float64
+│   ├── focal_spot_orientation_quat  (4 objects)
+│   │   ├── w  (2,), float64
+│   │   ├── x  (2,), float64
+│   │   ├── y  (2,), float64
+│   │   └── z  (2,), float64
+│   ├── focal_spot_position_mm  (3 objects)
+│   │   ├── x  (2,), float64
+│   │   ├── y  (2,), float64
+│   │   └── z  (2,), float64
+│   └── header  (2 objects)
+│       ├── timestamp  (2,), float64
+│       └── uuid  (2,), |S100
+└── source  (2 objects)
+    ├── current_ma  (256,), float64
+    └── voltage_kv  (256,), float64
+```
\ No newline at end of file
diff --git a/examples/h5_viewer.png b/examples/h5_viewer.png
new file mode 100644
index 0000000000000000000000000000000000000000..95a56cbabf6766b6ce9e846c3aeac938cbbe282a
Binary files /dev/null and b/examples/h5_viewer.png differ
diff --git a/examples/projection.h5 b/examples/projection.h5
index f1ad6b84c210a3aa483789e5e64199d863689462..034f5085310773948e4d7abde21b05f05529cbb7 100644
Binary files a/examples/projection.h5 and b/examples/projection.h5 differ
diff --git a/pyproject.toml b/pyproject.toml
index a6b85451af5f87b673311c03700d55fc0f3554a6..a213c230b6d653dabb1528b8c4dda2880a110a27 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -9,6 +9,7 @@ authors = [
 requires-python = ">=3.10"
 dependencies = [
     "h5py>=3.13.0",
+    "h5tree>=1.0",
     "jsonref>=1.1.0",
     "numpy==2.0.2",
     "pandas>=2.2.3",
diff --git a/scripts/add_joint_states_to_projection.py b/scripts/add_joint_states_to_projection.py
index bf82dac35060b4541c318c84b0d40f28cae299f0..c21a05ddf1c7726a19063045abfa3a195dbaac79 100644
--- a/scripts/add_joint_states_to_projection.py
+++ b/scripts/add_joint_states_to_projection.py
@@ -1,7 +1,6 @@
 from h5py import File
 import numpy as np
 from h5schemas.projection.add_projection import (
-    init_projection_dataset,
     add_projection_sample,
 )
 from h5schemas.thd_joint_states.add_thd_joint_states import (
@@ -14,16 +13,7 @@ def main():
     # Generate a file in write mode.
     test_file = File("./examples/projection.h5", "a")
 
-    # Initialize the .H5 structure
-    init_projection_dataset(test_file, False, (128, 128), (0.139, 0.139))
-
-    # # Add joint states group
-    # joint_states_group = test_file.require_group("/joint_states/")
-
-    # Init joint states group
-    init_thd_joint_states(test_file)
-
-    # Add dummy projections 1
+    # Add a new projection sample
     add_projection_sample(
         test_file,
         1e5 * np.random.random((128, 128)),
@@ -32,22 +22,10 @@ def main():
         [7.0, 8.0, 9.0],
         [10.0, 11.0, 12.0, 13],
     )
-    add_thd_joint_states_sample(test_file, *np.arange(15))
-
-    # # Add dummy projections 2
-    # add_projection_sample(
-    #     test_file,
-    #     1e5 * np.random.random((128, 128)),
-    #     np.array([0.0, 1.0, 2.0]),
-    #     np.array([3.0, 4.0, 5.0, 6.0]),
-    #     np.array([7.0, 8.0, 9.0]),
-    #     np.array([10.0, 11.0, 12.0, 13]),
-    #     voltage_kv=100,
-    # )
-    # add_thd_joint_states_sample(joint_states_group, *np.arange(15, 30))
 
-    # # Access data
-    # print(test_file["/image/image_array"][...])
+    # Add a new group
+    init_thd_joint_states(test_file)
+    add_thd_joint_states_sample(test_file, *np.arange(15))
 
 
 if __name__ == "__main__":
diff --git a/scripts/load_slice.py b/scripts/load_slice.py
new file mode 100644
index 0000000000000000000000000000000000000000..68c73df20a808c2d5f2fdfc743d3bae64e6e2c1e
--- /dev/null
+++ b/scripts/load_slice.py
@@ -0,0 +1,22 @@
+from h5py import File
+from h5schemas.load_utilities.projection import extract_projection, GeomOptions
+
+
+def main():
+    # Load the second projection from the dataset
+    test_file = File("./examples/projection.h5", "r")
+    projection = extract_projection(test_file, 1, GeomOptions.PROJECTION_GEOMETRY)
+    print(projection.image_array.shape)
+
+    # Load the first to projections an slice the projection data [20:50, 50:100:2]
+    projection = extract_projection(
+        test_file,
+        slice(0, 2),
+        GeomOptions.PROJECTION_GEOMETRY,
+        crop=(slice(20, 50), slice(50, 100, 2)),
+    )
+    print(projection.image_array.shape)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/src/h5schemas/load_utilities/projection.py b/src/h5schemas/load_utilities/projection.py
index d644d101f1f6af6d48a36fa1e8f6d1d181d6ebc3..d6f2914f1317c6161d93109c67fc36b4315c2888 100644
--- a/src/h5schemas/load_utilities/projection.py
+++ b/src/h5schemas/load_utilities/projection.py
@@ -1,4 +1,7 @@
-from h5schemas.load_utilities.projection_geometry import extract_projection_geometry, ProjectionGeometry
+from h5schemas.load_utilities.projection_geometry import (
+    extract_projection_geometry,
+    ProjectionGeometry,
+)
 from h5py import Group
 import numpy as np
 from typing import NamedTuple, ClassVar, Literal
@@ -24,13 +27,20 @@ class Projection(NamedTuple):
     timestamp: np.ndarray
 
 
-def extract_projection(h5_group: Group, indices: slice | int, projection_geometry_group: str = GeomOptions.PROJECTION_GEOMETRY, crop: list[slice] | None = None) -> Projection:
+def extract_projection(
+    h5_group: Group,
+    indices: slice | int,
+    projection_geometry_group: str = GeomOptions.PROJECTION_GEOMETRY,
+    crop: list[slice] | None = None,
+) -> Projection:
     if crop is None:
         crop = (slice(0, None, 1), slice(0, None, 1))
     if len(crop) != 3 and isinstance(indices, slice):
         crop = (slice(0, None), crop[0], crop[1])
 
-    projection_geometry = extract_projection_geometry(h5_group[projection_geometry_group], indices)
+    projection_geometry = extract_projection_geometry(
+        h5_group[projection_geometry_group], indices
+    )
     uuid = h5_group["image"]["header"]["uuid"][indices]
     timestamp = h5_group["image"]["header"]["timestamp"][indices]
     image = h5_group["image"]["image_array"][indices][crop]
@@ -51,6 +61,11 @@ if __name__ == "__main__":
     print(projection)
     print(projection.image_array.shape)
 
-    projection = extract_projection(test_file, slice(0, 2), GeomOptions.PROJECTION_GEOMETRY, crop=(slice(20, 50), slice(50, 100)))
+    projection = extract_projection(
+        test_file,
+        slice(0, 2),
+        GeomOptions.PROJECTION_GEOMETRY,
+        crop=(slice(20, 50), slice(50, 100)),
+    )
     print(projection)
     print(projection.image_array.shape)
diff --git a/src/h5schemas/load_utilities/projection_geometry.py b/src/h5schemas/load_utilities/projection_geometry.py
index 9fc22e490ffd3531b5cd0445a732115e30f91d1a..0df49d27d3fc5cf21d27af40bc8e247f96419818 100644
--- a/src/h5schemas/load_utilities/projection_geometry.py
+++ b/src/h5schemas/load_utilities/projection_geometry.py
@@ -14,7 +14,9 @@ class ProjectionGeometry(NamedTuple):
     timestamp: np.ndarray
 
 
-def extract_projection_geometry(h5_group: Group, indices: slice | int) -> ProjectionGeometry:
+def extract_projection_geometry(
+    h5_group: Group, indices: slice | int
+) -> ProjectionGeometry:
     detector_center_position_mm = extract_vector(
         h5_group["detector_center_position_mm"], indices, CommonVectors.XYZ
     )
diff --git a/uv.lock b/uv.lock
index 45678c87793b5e9721dccfc37a748a090db5fcd8..32df3014d7558e6da43e4e837b5989fd17f12e6f 100644
--- a/uv.lock
+++ b/uv.lock
@@ -97,6 +97,7 @@ version = "0.1.22"
 source = { editable = "." }
 dependencies = [
     { name = "h5py" },
+    { name = "h5tree" },
     { name = "jsonref" },
     { name = "numpy" },
     { name = "pandas" },
@@ -107,6 +108,7 @@ dependencies = [
 [package.metadata]
 requires-dist = [
     { name = "h5py", specifier = ">=3.13.0" },
+    { name = "h5tree", specifier = ">=1.0" },
     { name = "jsonref", specifier = ">=1.1.0" },
     { name = "numpy", specifier = "==2.0.2" },
     { name = "pandas", specifier = ">=2.2.3" },
@@ -114,6 +116,19 @@ requires-dist = [
     { name = "thd-json", git = "https://mygit.th-deg.de/roboct-public/roboct-schemas" },
 ]
 
+[[package]]
+name = "h5tree"
+version = "1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "h5py" },
+    { name = "termcolor" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ab/9b/ea46f715d5c5059de88ca2f87a63e7cb9a54f2a6e6cf56e88234730b179a/h5tree-1.0.tar.gz", hash = "sha256:d3c6ac2c647ebf4ba37ee91bdbd30ba147e68b0449c05ce1fe2ff9e7e61d8e12", size = 3418 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/d6/f6/949bb1616da797ffe2badb6d09ee9664387c27105c28bac59917467c6254/h5tree-1.0-py3-none-any.whl", hash = "sha256:e7b83c6ea25fbd40dc05e8cf182aa43bde9bae19d88f7bed0308ff621d5c337e", size = 3466 },
+]
+
 [[package]]
 name = "hatchling"
 version = "1.27.0"
@@ -697,6 +712,15 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/89/fd/62f31643596f6ab71fc6d2a87acdee0bc01a03fbe1a7f3f6dc0c91e2546d/tables-3.10.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:757c6ea257c174af8036cf8f273ede756bbcd6db5ac7e2a4d64e788b0f371152", size = 7527953 },
 ]
 
+[[package]]
+name = "termcolor"
+version = "2.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/37/72/88311445fd44c455c7d553e61f95412cf89054308a1aa2434ab835075fc5/termcolor-2.5.0.tar.gz", hash = "sha256:998d8d27da6d48442e8e1f016119076b690d962507531df4890fcd2db2ef8a6f", size = 13057 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/7f/be/df630c387a0a054815d60be6a97eb4e8f17385d5d6fe660e1c02750062b4/termcolor-2.5.0-py3-none-any.whl", hash = "sha256:37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8", size = 7755 },
+]
+
 [[package]]
 name = "thd-json"
 version = "0.1.14"