diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index f5d7881..3149482 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -1,6 +1,6 @@
 # Contributors
 
 - Hartmut Seichter (dev lead)
-- Robin Rottstädt (image loader)
+- Robin Rottstädt (Flappy Bird, image loader)
 - Tim Gösselmann (Win32 fixes)
-- Hannes Brothuhn (Flappy Bird clone)
+- Hannes Brothuhn (Flappy Bird fixes)
diff --git a/examples/quickwings/quickwings.cpp b/examples/quickwings/quickwings.cpp
index dd51112..8564040 100644
--- a/examples/quickwings/quickwings.cpp
+++ b/examples/quickwings/quickwings.cpp
@@ -5,6 +5,7 @@
  *
  */
 
+#include <filesystem>
 #include <paradiso/bitmap.hpp>
 #include <paradiso/bitmap_io.hpp>
 #include <paradiso/context.hpp>
@@ -16,8 +17,104 @@
 #include <paradiso/vector.hpp>
 #include <paradiso/window.hpp>
 
+#include <array>
 #include <chrono>
+#include <flat_map>
+#include <print>
+#include <string_view>
 #include <thread>
+#include <tuple>
+#include <unordered_map>
+#include <vector>
+
+namespace QuickWings {
+
+using SpriteName = std::tuple<std::string_view, std::string_view>;
+using SpriteMap = std::unordered_map<std::string_view, paradiso::Bitmap>;
+
+struct App {
+    SpriteMap sprites;
+
+    static auto create() -> App {
+
+        paradiso::BitmapIO::get().set_asset_path(
+            paradiso::get_executable_path().parent_path() / "assets");
+
+        auto assets = std::array{
+            SpriteName{"background.bg", "background-day.png"},
+            SpriteName{"background.fg", "base.png"},
+        };
+
+        SpriteMap load_map{};
+
+        for (const auto& [name, filename] : assets) {
+            std::print("{} : {}", name, filename);
+
+            if (auto bm = paradiso::BitmapIO::get().load(filename);
+                bm.has_value()) {
+                load_map[name] = bm.value();
+                std::print(" ok!\n");
+            } else {
+                std::print(" error!\n");
+            }
+        }
+        return {.sprites{load_map}};
+    }
+
+    void run() {
+        auto window = paradiso::Window();
+
+        paradiso::Size size {.width = 640, .height = 480};
+
+        window
+            .set_size(size) // ... Grösse
+            .set_position(
+                paradiso::Point{.x = 1920 / 2 - 500 / 2,
+                                .y = 1080 / 2 - 700 / 2}) // ... Position
+            .set_title("PardiSO.Quickwings")              // ... Titel
+            .set_visible(true); // ... und jetzt anzeigen!
+
+        // der Fenster Kontext
+        auto ctx = paradiso::Context{};
+
+        // ein Shader (Schattierungsprogramm)
+        auto shader = paradiso::Shader{};
+
+        // wir nutzen einen vorgefertigten shader
+        shader.load_preset(paradiso::Shader::Preset::Sprite);
+
+        // ein viewport stellt die Sicht der Kamera dar, d.h. bei quadratischen
+        // Pixeln sollte hier auch eine dementsprechende Grösse eingestellt
+        // werden
+        ctx.set_viewport(
+            paradiso::Rectangle{.position = paradiso::Point{.x = 0, .y = 0},
+                                .size = window.size()});
+
+        // nothing beats a classic look
+        ctx.set_clearcolor(paradiso::RGBA::from_rgb(0x00, 0x00, 0x00));
+
+        while (
+            window.update([&](paradiso::Window& w) -> bool {
+            ctx.set_viewport(paradiso::Rectangle{
+                .position = paradiso::Point{.x = 0, .y = 0},
+                .size = size,
+            });
+
+            // Quit
+            return !(w.keyboard_input().size() &&
+                     w.keyboard_input().top().key == 'Q');
+        }){};
+
+    }
+
+    } // namespace QuickWings
+
+    auto main() -> int {
+        auto app = QuickWings::App::create();
+        app.run();
+    }
+
+#if 0
 
 // TODO remove all hard coded 'magic' values!
 
@@ -31,7 +128,12 @@ float risky_pos_top_y;
 float risky_pos_bottom_max_y;
 float risky_pos_top_max_y;
 
+
+
 struct Background {
+
+    SpriteMap sprites;
+
     paradiso::Sprite backgroundLeft;
     paradiso::Sprite backgroundRight;
     paradiso::Sprite grassLeft;
@@ -44,7 +146,9 @@ struct Background {
     // TODO no constructors in rule of zero
     Background() {
         auto backgroundImage =
-            paradiso::BitmapIO::get().load("background-day.png");
+            paradiso::BitmapIO::get().load("background-day.png").has_value()
+                ? paradiso::BitmapIO::get().load("background-day.png").value()
+                : paradiso::Bitmap();
         backgroundLeft = paradiso::Sprite{
             .bitmap = backgroundImage,
             .pivot = {paradiso::Vector2<float>::make(0.0f, 0.16f)},
@@ -459,7 +563,7 @@ auto main() -> int {
         .set_size(size) // ... Grösse
         .set_position(paradiso::Point{.x = 1920 / 2 - 500 / 2,
                                       .y = 1080 / 2 - 700 / 2}) // ... Position
-        .set_title("PardiSO.FlappyBird")                        // ... Titel
+        .set_title("PardiSO.Quickwings")                        // ... Titel
         .set_visible(true); // ... und jetzt anzeigen!
 
     // der Fenster Kontext
@@ -482,7 +586,8 @@ auto main() -> int {
 
     // Asset loader bekommt den Pfad
 
-    paradiso::BitmapIO::get().set_path("assets");
+    paradiso::BitmapIO::get().set_asset_path(
+        paradiso::get_executable_path().parent_path() / "assets");
 
     // Load
     auto background = Background{};
@@ -544,3 +649,4 @@ auto main() -> int {
 
     return 0;
 }
+#endif
diff --git a/src/lib/include/paradiso/bitmap_io.hpp b/src/lib/include/paradiso/bitmap_io.hpp
index 1700bc1..1dd77b9 100644
--- a/src/lib/include/paradiso/bitmap_io.hpp
+++ b/src/lib/include/paradiso/bitmap_io.hpp
@@ -23,8 +23,11 @@
 #ifndef PARADISO_BITMAP_IO_HPP
 #define PARADISO_BITMAP_IO_HPP
 
+#include <filesystem>
 #include <paradiso/bitmap.hpp>
+#include <paradiso/utils.hpp>
 
+#include <expected>
 #include <memory>
 #include <string_view>
 
@@ -32,13 +35,23 @@ namespace paradiso {
 
 struct BitmapIO {
 
+    enum struct LoadError {
+        FileNotFound,
+        FileCorrupted,
+        FileNotBitmap,
+    };
+
+    using LoadResult = std::expected<Bitmap, LoadError>;
+    using path = std::filesystem::path;
+
     static BitmapIO& get();
 
-    Bitmap load(std::string_view filename, bool ignore_cache = false) const;
+    auto load(std::string_view filename, bool ignore_cache = false) const
+        -> LoadResult;
 
-    void set_path(std::string_view path);
+    void set_asset_path(path path_);
 
-    std::string path() const;
+    path asset_path() const;
 
   private:
     struct Impl;
diff --git a/src/lib/src/bitmap_io.cpp b/src/lib/src/bitmap_io.cpp
index b279244..35510e3 100644
--- a/src/lib/src/bitmap_io.cpp
+++ b/src/lib/src/bitmap_io.cpp
@@ -26,15 +26,14 @@
 #include "paradiso/bitmap_io.hpp"
 #include "paradiso/bitmap.hpp"
 
+#include <filesystem>
+#include <string>
 #include <unordered_map>
 
 // STB image loading
 #define STB_IMAGE_IMPLEMENTATION
 #include "stb_image.h"
 
-#include <filesystem>
-#include <iostream>
-
 namespace paradiso {
 
 struct BitmapIO::Impl {
@@ -45,7 +44,7 @@ struct BitmapIO::Impl {
 
     BitmapCacheType cache_;
 
-    static Bitmap read(std::string_view filename) {
+    static BitmapIO::LoadResult _read_bitmap(const path& filepath) {
 
         // Load with stb_image
         stbi_set_flip_vertically_on_load(true); // flip y axis for OpenGL
@@ -54,16 +53,16 @@ struct BitmapIO::Impl {
 
         auto stb_free = [](uint8_t* data) { stbi_image_free(data); };
 
-        if (std::unique_ptr<uint8_t, decltype(stb_free)> image{
-                stbi_load(filename.data(), &width, &height, &channels, 0)};
+        if (std::unique_ptr<uint8_t, decltype(stb_free)> image{stbi_load(
+                filepath.string().c_str(), &width, &height, &channels, 0)};
             image.get()) {
 
-            int size = width * height;
+            const auto area = width * height;
 
             // Convert to Vector of RGBA
             std::vector<paradiso::RGBA> rgba =
-                std::vector<paradiso::RGBA>(size);
-            for (int i = 0; i < size; i++) {
+                std::vector<paradiso::RGBA>(area);
+            for (int i = 0; i < area; i++) {
                 // get rgba values
                 int pos = i * 4;
                 int r = image.get()[pos + 0];
@@ -72,47 +71,50 @@ struct BitmapIO::Impl {
                 int a = image.get()[pos + 3];
 
                 // bug in from_rgba. it expects bgra, not rgba
-                auto val = paradiso::RGBA::from_rgba(b, g, r, a);
-                rgba[i] = val;
+                // auto val = paradiso::RGBA::from_rgba(b, g, r, a);
+                rgba[i] = paradiso::RGBA::from_rgba(r, g, b, a);
             }
-
-            // rotate image by 90 degrees CW
-            std::vector<paradiso::RGBA> rotated =
-                std::vector<paradiso::RGBA>(size);
-            for (int x = 0; x < width; x++) {
-                for (int y = 0; y < height; y++) {
-
-                    int pos = y * width + x;
-                    int pos_rotated = x * height + (height - y - 1);
-                    rotated[pos_rotated] = rgba[pos];
-                }
-            }
-
             return paradiso::Bitmap::from_data(
                 paradiso::Size{static_cast<unsigned int>(height),
                                static_cast<unsigned int>(width)},
-                rotated);
+                rgba);
+
+            // rotate image by 90 degrees CW
+            // std::vector<paradiso::RGBA> rotated =
+            //     std::vector<paradiso::RGBA>(size);
+            // for (int x = 0; x < width; x++) {
+            //     for (int y = 0; y < height; y++) {
+
+            //         int pos = y * width + x;
+            //         int pos_rotated = x * height + (height - y - 1);
+            //         rotated[pos_rotated] = rgba[pos];
+            //     }
+            // }
+
+            // return paradiso::Bitmap::from_data(
+            //     paradiso::Size{static_cast<unsigned int>(height),
+            //                    static_cast<unsigned int>(width)},
+            //     rotated);
         } else {
-            std::cerr << "cannot load " << filename << '\n';
+            return std::unexpected(BitmapIO::LoadError::FileNotFound);
         }
 
         return Bitmap(); //  well, no exception ...
     }
 
-    Bitmap load(std::string_view filename) {
-        if (cache_.find(filename) == cache_.end()) {
+    auto load(std::string_view filename, bool ignore_cache) -> LoadResult {
+        // check cache first
+        if (cache_.find(filename) != cache_.end() && !ignore_cache) {
+            return cache_[filename.data()];
+        } // construct file path
+        auto asset_filepath = asset_path_ / std::filesystem::path(filename);
 
-            auto asset_filepath = asset_path_ / std::filesystem::path(filename);
+        auto data = _read_bitmap(asset_filepath);
 
-            std::cout << "Loading " << asset_filepath << std::endl;
-
-            // moving to C++23 this should be replaced with
-            // std::expected
-            auto data = read(asset_filepath.generic_string());
-
-            cache_[filename] = data;
+        if (data.has_value()) {
+            cache_[filename.data()] = *data;
         }
-        return cache_[filename.data()];
+        return data;
     }
 };
 
@@ -123,14 +125,15 @@ BitmapIO& BitmapIO::get() {
     return instance;
 }
 
-Bitmap BitmapIO::load(std::string_view filename, bool ignore_cache) const {
-    return impl_->load(filename);
+auto BitmapIO::load(std::string_view filename, bool ignore_cache) const
+    -> BitmapIO::LoadResult {
+    return impl_->load(filename, ignore_cache);
 }
 
-void BitmapIO::set_path(std::string_view path) { impl_->asset_path_ = path; }
-
-std::string BitmapIO::path() const {
-    return impl_->asset_path_.generic_string();
+void BitmapIO::set_asset_path(BitmapIO::path path_) {
+    impl_->asset_path_ = path_;
 }
 
+BitmapIO::path BitmapIO::asset_path() const { return impl_->asset_path_; }
+
 } // namespace paradiso