diff --git a/.github/workflows/arch.yml b/.github/workflows/arch.yml
index 543967c..ad945d8 100644
--- a/.github/workflows/arch.yml
+++ b/.github/workflows/arch.yml
@@ -17,7 +17,7 @@ jobs:
         run: |
           sed -i 's/SigLevel    = Required DatabaseOptional/SigLevel    = Optional TrustAll/' /etc/pacman.conf
           pacman --noconfirm --noprogressbar -Syyu
-          pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang libc++ pixman cairo hyprutils libjpeg-turbo libjxl libwebp
+          pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang libc++ pixman cairo hyprutils libjpeg-turbo libjxl libwebp libspng
 
       - name: Build hyprgraphics with gcc
         run: |
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 072b7b4..492db50 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -47,7 +47,8 @@ pkg_check_modules(
   hyprutils
   libjpeg
   libwebp
-  libmagic)
+  libmagic
+  spng)
 
 pkg_check_modules(
   JXL
diff --git a/README.md b/README.md
index 223c12e..a930831 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,7 @@ Dep list:
  - libjxl_cms [optional]
  - libjxl_threads [optional]
  - libmagic
+ - libspng
 
 ## Building
 
diff --git a/src/image/Image.cpp b/src/image/Image.cpp
index 82467e9..f87d088 100644
--- a/src/image/Image.cpp
+++ b/src/image/Image.cpp
@@ -5,6 +5,7 @@
 #include "formats/JpegXL.hpp"
 #endif
 #include "formats/Webp.hpp"
+#include "formats/Png.hpp"
 #include <magic.h>
 #include <format>
 
@@ -15,7 +16,7 @@ Hyprgraphics::CImage::CImage(const std::string& path) : filepath(path) {
     std::expected<cairo_surface_t*, std::string> CAIROSURFACE;
     const auto                                   len = path.length();
     if (path.find(".png") == len - 4 || path.find(".PNG") == len - 4) {
-        CAIROSURFACE = cairo_image_surface_create_from_png(path.c_str());
+        CAIROSURFACE = PNG::createSurfaceFromPNG(path);
         mime = "image/png";
     } else if (path.find(".jpg") == len - 4 || path.find(".JPG") == len - 4 || path.find(".jpeg") == len - 5 || path.find(".JPEG") == len - 5) {
         CAIROSURFACE  = JPEG::createSurfaceFromJPEG(path);
@@ -47,7 +48,7 @@ Hyprgraphics::CImage::CImage(const std::string& path) : filepath(path) {
         const auto first_word = type_str.substr(0, type_str.find(" "));
 
         if (first_word == "PNG") {
-            CAIROSURFACE = cairo_image_surface_create_from_png(path.c_str());
+            CAIROSURFACE = PNG::createSurfaceFromPNG(path);
             mime = "image/png";
         } else if (first_word == "JPEG") {
             CAIROSURFACE  = JPEG::createSurfaceFromJPEG(path);
diff --git a/src/image/formats/Png.cpp b/src/image/formats/Png.cpp
new file mode 100644
index 0000000..7a01379
--- /dev/null
+++ b/src/image/formats/Png.cpp
@@ -0,0 +1,77 @@
+#include "Png.hpp"
+#include <spng.h>
+#include <vector>
+#include <fstream>
+#include <filesystem>
+#include <cstdint>
+#include <hyprutils/utils/ScopeGuard.hpp>
+using namespace Hyprutils::Utils;
+
+static std::vector<unsigned char> readBinaryFile(const std::string& filename) {
+    std::ifstream f(filename, std::ios::binary);
+    if (!f.good())
+        return {};
+    f.unsetf(std::ios::skipws);
+    return {std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>()};
+}
+
+std::expected<cairo_surface_t*, std::string> PNG::createSurfaceFromPNG(const std::string& path) {
+    if (!std::filesystem::exists(path))
+        return std::unexpected("loading png: file doesn't exist");
+
+    spng_ctx*   ctx = spng_ctx_new(0);
+
+    CScopeGuard x([&] { spng_ctx_free(ctx); });
+
+    const auto  PNGCONTENT = readBinaryFile(path);
+
+    if (PNGCONTENT.empty())
+        return std::unexpected("loading png: file content was empty (bad file?)");
+
+    spng_set_png_buffer(ctx, PNGCONTENT.data(), PNGCONTENT.size());
+
+    spng_ihdr ihdr{0};
+    if (spng_get_ihdr(ctx, &ihdr))
+        return std::unexpected("loading png: file content was empty (bad file?)");
+
+    int fmt = SPNG_FMT_PNG;
+    if (ihdr.color_type == SPNG_COLOR_TYPE_INDEXED)
+        fmt = SPNG_FMT_RGB8;
+
+    size_t imageLength = 0;
+    if (spng_decoded_image_size(ctx, fmt, &imageLength))
+        return std::unexpected("loading png: spng_decoded_image_size failed");
+
+    uint8_t* imageData = (uint8_t*)malloc(imageLength);
+
+    if (!imageData)
+        return std::unexpected("loading png: mallocing failed, out of memory?");
+
+    // TODO: allow proper decode of high bitrate images
+    if (spng_decode_image(ctx, imageData, imageLength, SPNG_FMT_RGBA8, 0)) {
+        free(imageData);
+        return std::unexpected("loading png: spng_decode_image failed (invalid image?)");
+    }
+
+    // convert RGBA8888 -> ARGB8888 premult for cairo
+    for (size_t i = 0; i < imageLength; i += 4) {
+        uint8_t r, g, b, a;
+        a = ((*((uint32_t*)(imageData + i))) & 0xFF000000) >> 24;
+        b = ((*((uint32_t*)(imageData + i))) & 0x00FF0000) >> 16;
+        g = ((*((uint32_t*)(imageData + i))) & 0x0000FF00) >> 8;
+        r = (*((uint32_t*)(imageData + i))) & 0x000000FF;
+
+        r *= ((float)a / 255.F);
+        g *= ((float)a / 255.F);
+        b *= ((float)a / 255.F);
+
+        *((uint32_t*)(imageData + i)) = (((uint32_t)a) << 24) | (((uint32_t)r) << 16) | (((uint32_t)g) << 8) | (uint32_t)b;
+    }
+
+    auto CAIROSURFACE = cairo_image_surface_create_for_data(imageData, CAIRO_FORMAT_ARGB32, ihdr.width, ihdr.height, ihdr.width * 4);
+
+    if (!CAIROSURFACE)
+        return std::unexpected("loading png: cairo failed");
+
+    return CAIROSURFACE;
+}
\ No newline at end of file
diff --git a/src/image/formats/Png.hpp b/src/image/formats/Png.hpp
new file mode 100644
index 0000000..dec8ee3
--- /dev/null
+++ b/src/image/formats/Png.hpp
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <cairo/cairo.h>
+#include <string>
+#include <expected>
+
+namespace PNG {
+    std::expected<cairo_surface_t*, std::string> createSurfaceFromPNG(const std::string&);
+};
\ No newline at end of file
diff --git a/tests/image.cpp b/tests/image.cpp
index 7067f9a..42a1082 100644
--- a/tests/image.cpp
+++ b/tests/image.cpp
@@ -16,7 +16,16 @@ bool tryLoadImage(const std::string& path) {
 
     std::println("Loaded {} successfully: Image is {}x{} of type {}", path, image.cairoSurface()->size().x, image.cairoSurface()->size().y, image.getMime());
 
-    return true;
+    const auto TEST_DIR = std::filesystem::current_path().string() + "/test_output";
+
+    // try to write it for inspection
+    if (!std::filesystem::exists(TEST_DIR))
+        std::filesystem::create_directory(TEST_DIR);
+
+    std::string name = image.getMime();
+    std::replace(name.begin(), name.end(), '/', '_');
+
+    return cairo_surface_write_to_png(image.cairoSurface()->cairo(), (TEST_DIR + "/" + name + ".png").c_str()) == CAIRO_STATUS_SUCCESS;
 }
 
 int main(int argc, char** argv, char** envp) {
@@ -27,7 +36,8 @@ int main(int argc, char** argv, char** envp) {
             continue;
         auto expectation = true;
 #ifndef JXL_FOUND
-        if (file.path().filename() == "hyprland.jxl") expectation = false;
+        if (file.path().filename() == "hyprland.jxl")
+            expectation = false;
 #endif
         EXPECT(tryLoadImage(file.path()), expectation);
     }