diff --git a/Brillo/Juicy.hs b/Brillo/Juicy.hs
new file mode 100644
--- /dev/null
+++ b/Brillo/Juicy.hs
@@ -0,0 +1,166 @@
+{-# LANGUAGE TupleSections #-}
+
+module Brillo.Juicy (
+  -- * Conversion from JuicyPixels' types to brillo' Picture
+  fromDynamicImage,
+  fromImageRGBA8,
+  fromImageRGB8,
+  fromImageY8,
+  fromImageYA8,
+  fromImageYCbCr8,
+
+  -- * Loading a brillo Picture from a file through JuicyPixels
+  loadJuicy,
+  loadJuicyWithMetadata,
+  loadJuicyJPG,
+  loadJuicyPNG,
+
+  -- * From brillo, exported here for convenience
+  loadBMP,
+)
+where
+
+import Brillo.Data.Bitmap (
+  BitmapFormat (BitmapFormat),
+  PixelFormat (PxRGBA),
+  RowOrder (TopToBottom),
+  bitmapOfForeignPtr,
+  loadBMP,
+ )
+import Brillo.Data.Picture (Picture)
+import Codec.Picture (
+  DynamicImage (
+    ImageRGB8,
+    ImageRGBA8,
+    ImageRGBF,
+    ImageY8,
+    ImageYA8,
+    ImageYCbCr8,
+    ImageYF
+  ),
+  Image (..),
+  Pixel8,
+  PixelRGB8,
+  PixelRGBA8,
+  PixelYA8,
+  PixelYCbCr8,
+  readImage,
+  readImageWithMetadata,
+  readJpeg,
+  readPng,
+ )
+import Codec.Picture.Metadata (Metadatas)
+import Codec.Picture.Types (
+  ColorConvertible (promoteImage),
+  ColorSpaceConvertible (convertImage),
+ )
+import Data.Vector.Storable (unsafeToForeignPtr)
+
+
+{-| Tries to convert a 'DynamicImage' from JuicyPixels to a brillo 'Picture'.
+All formats except RGBF and YF should successfully yield a 'Picture'.
+-}
+fromDynamicImage :: DynamicImage -> Maybe Picture
+fromDynamicImage (ImageY8 img) = Just $ fromImageY8 img
+fromDynamicImage (ImageYA8 img) = Just $ fromImageYA8 img
+fromDynamicImage (ImageRGB8 img) = Just $ fromImageRGB8 img
+fromDynamicImage (ImageRGBA8 img) = Just $ fromImageRGBA8 img
+fromDynamicImage (ImageYCbCr8 img) = Just $ fromImageYCbCr8 img
+fromDynamicImage (ImageRGBF _) = Nothing
+fromDynamicImage (ImageYF _) = Nothing
+fromDynamicImage _ = Nothing
+
+
+{-| O(N) conversion from 'PixelRGBA8' image to brillo 'Picture',
+where N is the number of pixels.
+-}
+fromImageRGBA8 :: Image PixelRGBA8 -> Picture
+fromImageRGBA8 (Image{imageWidth = w, imageHeight = h, imageData = imgDat}) = do
+  let (ptr, _, _) = unsafeToForeignPtr imgDat
+  bitmapOfForeignPtr w h (BitmapFormat TopToBottom PxRGBA) ptr True
+{-# INLINE fromImageRGBA8 #-}
+
+
+{-| Creation of a brillo 'Picture' by promoting (through 'promoteImage')
+the 'PixelRGB8' image to 'PixelRGBA8' and calling 'fromImageRGBA8'.
+-}
+fromImageRGB8 :: Image PixelRGB8 -> Picture
+fromImageRGB8 = fromImageRGBA8 . promoteImage
+{-# INLINE fromImageRGB8 #-}
+
+
+{-| Creation of a brillo 'Picture' by promoting (through 'promoteImage')
+the 'PixelY8' image to 'PixelRGBA8' and calling 'fromImageRGBA8'.
+-}
+fromImageY8 :: Image Pixel8 -> Picture
+fromImageY8 = fromImageRGBA8 . promoteImage
+{-# INLINE fromImageY8 #-}
+
+
+{-| Creation of a brillo 'Picture' by promoting (through 'promoteImage')
+the 'PixelYA8' image to 'PixelRGBA8' and calling 'fromImageRGBA8'.
+-}
+fromImageYA8 :: Image PixelYA8 -> Picture
+fromImageYA8 = fromImageRGBA8 . promoteImage
+{-# INLINE fromImageYA8 #-}
+
+
+{-| Creation of a brillo 'Picture' by promoting (through 'promoteImage')
+the 'PixelYCbCr8' image to 'PixelRGBA8' and calling 'fromImageRGBA8'.
+-}
+fromImageYCbCr8 :: Image PixelYCbCr8 -> Picture
+fromImageYCbCr8 = fromImageRGB8 . convertImage
+{-# INLINE fromImageYCbCr8 #-}
+
+
+{-| Tries to load an image file into a Picture
+  using 'readImage' from JuicyPixels.
+  It means it'll try to successively read the content
+  as an image in the following order,
+  until it succeeds (or fails on all of them):
+  jpeg, png, bmp, gif, hdr (the last two are not supported)
+  This is handy when you don't know
+  what format the image contained in the file is encoded with.
+  If you know the format in advance,
+  use 'loadBMP', 'loadJuicyJPG' or 'loadJuicyPNG'
+-}
+loadJuicy :: FilePath -> IO (Maybe Picture)
+loadJuicy = loadWith readImage
+{-# INLINE loadJuicy #-}
+
+
+loadJuicyWithMetadata :: FilePath -> IO (Maybe (Picture, Metadatas))
+loadJuicyWithMetadata = loadWithMetadata readImageWithMetadata
+{-# INLINE loadJuicyWithMetadata #-}
+
+
+loadJuicyJPG :: FilePath -> IO (Maybe Picture)
+loadJuicyJPG = loadWith readJpeg
+{-# INLINE loadJuicyJPG #-}
+
+
+loadJuicyPNG :: FilePath -> IO (Maybe Picture)
+loadJuicyPNG = loadWith readPng
+{-# INLINE loadJuicyPNG #-}
+
+
+loadWith
+  :: (FilePath -> IO (Either String DynamicImage))
+  -> FilePath
+  -> IO (Maybe Picture)
+loadWith reader fp = do
+  eImg <- reader fp
+  return $ either (const Nothing) fromDynamicImage eImg
+
+
+loadWithMetadata
+  :: (FilePath -> IO (Either String (DynamicImage, Metadatas)))
+  -> FilePath
+  -> IO (Maybe (Picture, Metadatas))
+loadWithMetadata reader fp = do
+  eImg <- reader fp
+  return $
+    either
+      (const Nothing)
+      (\(x, y) -> fmap (,y) (fromDynamicImage x))
+      eImg
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2013-2024, Alp Mestanogullari <alpmestan@gmail.com>,
+Copyright (c) 2024-2025, Adrian Sieber
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,4 @@
+import Distribution.Simple
+
+
+main = defaultMain
diff --git a/brillo-juicy.cabal b/brillo-juicy.cabal
new file mode 100644
--- /dev/null
+++ b/brillo-juicy.cabal
@@ -0,0 +1,47 @@
+cabal-version: 3.0
+name:          brillo-juicy
+version:       0.2.4
+synopsis:
+  Load any image supported by Juicy.Pixels in your brillo application
+
+description:
+  Lets you convert any image supported by Juicy.Pixels in a brillo application
+  by converting to brillo' Bitmap representation.
+  Version 0.1.2 just enabled caching (from a frame to the next, and so on)
+  of the images you load, so that they are not recomputed for each frame.
+  Credits go to Jonathan Daugherty for the patch.
+
+homepage:      https://github.com/ad-si/Brillo
+license:       BSD-3-Clause
+license-file:  LICENSE
+author:        Alp Mestanogullari <alpmestan@gmail.com>, Adrian Sieber
+maintainer:    Adrian Sieber
+category:      Graphics
+build-type:    Simple
+
+library
+  default-language: GHC2021
+  exposed-modules:  Brillo.Juicy
+  build-depends:
+    , base         >=4.8    && <5
+    , bmp          >=1.2    && <1.3
+    , brillo       >=1.13.3 && <1.15
+    , bytestring   >=0.11   && <0.12
+    , JuicyPixels  >=3.3    && <3.4
+    , vector       >=0.13   && <0.14
+
+  ghc-options:      -O2 -Wall
+
+executable brillo-juicy-viewer
+  default-language: GHC2021
+  main-is:          brillo-juicy.hs
+  build-depends:
+    , base         >=4.8    && <5
+    , bmp          >=1.2    && <1.3
+    , brillo       >=1.13.3 && <1.15
+    , bytestring   >=0.11   && <0.12
+    , JuicyPixels  >=3.3    && <3.4
+    , vector       >=0.13   && <0.14
+
+  ghc-options:      -O2 -Wall -threaded
+  other-modules:    Brillo.Juicy
diff --git a/brillo-juicy.hs b/brillo-juicy.hs
new file mode 100644
--- /dev/null
+++ b/brillo-juicy.hs
@@ -0,0 +1,32 @@
+module Main where
+
+import Brillo (
+  BitmapData (bitmapSize),
+  Display (InWindow),
+  Picture (Bitmap),
+  display,
+  white,
+ )
+import Brillo.Juicy (loadJuicy)
+import System.Environment (getArgs)
+
+
+main :: IO ()
+main = do
+  args <- getArgs
+  case args of
+    [filename] ->
+      loadJuicy filename
+        >>= maybe
+          (putStrLn $ "Couldn't load or decode " ++ filename)
+          displayPic
+    _ ->
+      putStrLn
+        "Usage: brillo-juicy <file> -- Displays the image in a Brillo window"
+
+
+displayPic :: Picture -> IO ()
+displayPic p@(Bitmap dta) = do
+  let (width, height) = bitmapSize dta
+  display (InWindow "Image Viewer" (width, height) (10, 10)) white p
+displayPic _ = error "only the Bitmap constructor should be used here"
