diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,13 @@
 
 h-raylib's version numbers do not follow the usual format. The first two numbers in the version track the underlying C raylib version. For example, `5.1.x.x` versions use raylib 5.1 under the hood. The third number represents breaking changes (renamed/deleted functions or modules). The last number represents non-breaking changes (new functions or modules, bug fixes, etc). The safest version bound format to use is `h-raylib >=x.y.z.w && <x.y.(z+1)` (instead of the usual `^>=` bound).
 
+## Version 5.5.3.0
+_14 August 2025_
+
+- **BREAKING CHANGE**: Bracket functions now only work with the `IO` monad
+- \[[#60](https://github.com/Anut-py/h-raylib/issues/60)\] Added the `disable-lens` flag
+- Loosened version bounds
+
 ## Version 5.5.2.1
 _28 October 2024_
 
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -25,24 +25,3 @@
 ### In progress
 
 - Add web build support \[[#4](https://github.com/Anut-py/h-raylib/issues/4)\]
-
-### Implemented
-
-Items which have been completed but not published to hackage.
-
-(none)
-
-### Published
-
-Items which have been published to hackage.
-
-- Implement automatic memory management for callbacks (`5.1.1.0`)
-- Move Raylib.Internal.Native functions into the modules where they are called (`5.1.1.0`)
-  - Use Template Haskell to clean up boilerplate
-- Split Raylib.Types into multiple modules (`5.1.1.0`)
-- Bind `raygui` \[[#34](https://github.com/Anut-py/h-raylib/issues/34)\]  (`5.1.1.0`)
-- Bind `raymath` (`4.6.0.1`)
-- Bind `rcamera` (`4.6.0.1`)
-- Bind `rlgl` (`4.5.3.2`)
-- Allow manual unloading of assets for larger projects (`4.5.3.1`)
-- Make it easier to pass shader parameters (`4.5.3.0`)
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -81,6 +81,9 @@
 The flags `platform-windows`, `platform-mac`, `platform-linux`, and `platform-bsd` are also
 supported if you want to build for a different platform.
 
+If you prefer not to use the `lens` package, you may enable the `disable-lens` flag. This
+removes the dependency on `lens` and disables `Raylib.Util.Lenses`.
+
 ## Running in GHCi
 
 You can use this library in GHCi just like any other library, but you will need to add `--constraint="h-raylib +ghci"` to the command. For example, in the root folder of this repository, you could use the command below to use the library through GHCi.
diff --git a/default.nix b/default.nix
--- a/default.nix
+++ b/default.nix
@@ -3,7 +3,7 @@
 }:
 mkDerivation {
   pname = "h-raylib";
-  version = "5.5.2.1";
+  version = "5.5.3.0";
   src = ./.;
   isLibrary = true;
   isExecutable = buildExamples;
diff --git a/h-raylib.cabal b/h-raylib.cabal
--- a/h-raylib.cabal
+++ b/h-raylib.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.4
 name:               h-raylib
-version:            5.5.2.1
+version:            5.5.3.0
 synopsis:           Raylib bindings for Haskell
 category:           graphics
 description:
@@ -45,7 +45,7 @@
 
 source-repository head
   type:     git
-  location: git://github.com/Anut-py/h-raylib.git
+  location: https://github.com/Anut-py/h-raylib.git
 
 flag detect-platform
   description:
@@ -101,6 +101,11 @@
   default:     False
   manual:      True
 
+flag disable-lens
+  description: Exclude the lens dependency and Raylib.Util.Lenses
+  default:     False
+  manual:      True
+
 common example-options
   default-language: Haskell2010
 
@@ -229,24 +234,34 @@
     Raylib.Util.Colors
     Raylib.Util.GUI
     Raylib.Util.GUI.Styles
-    Raylib.Util.Lenses
     Raylib.Util.Math
     Raylib.Util.RLGL
 
+  if !flag(disable-lens)
+    exposed-modules:
+      Raylib.Util.Lenses
+
   other-modules:
     Raylib.Internal.TH
     Raylib.Internal.Web.Native
     Raylib.Internal.Web.Processable
 
+  if !flag(disable-lens)
+    other-modules:
+      Raylib.Util.Lenses.TH
+  
   build-depends:
     , base              >=4.0      && <4.22
     , bytestring        >=0.11.0   && <0.13
-    , containers        >=0.6.0    && <0.7
-    , exceptions        >=0.10.4   && <0.11
-    , lens              >=4.0      && <5.4
-    , linear            >=1.22     && <1.24
-    , template-haskell  >=2.16.0.0 && <2.23.0.0
+    , containers        >=0.6.0    && <0.9
+    , template-haskell  >=2.16.0.0 && <2.24
     , text              >=2.0      && <2.2
+  
+  
+  if !flag(disable-lens)
+    build-depends:
+      , linear            >=1.22     && <1.24
+      , lens              >=4.0      && <5.4
 
   hs-source-dirs:   src
   default-language: Haskell2010
@@ -325,7 +340,7 @@
 
   else
     cc-options:
-      -DPLATFORM_DESKTOP_GLFW -Wno-int-to-void-pointer-cast
+      -DPLATFORM_DESKTOP_GLFW -Wno-int-to-void-pointer-cast -Wno-discarded-qualifiers
 
     c-sources:
       lib/rgui_bindings.c
@@ -363,3 +378,6 @@
 
   if flag(ghci)
     cpp-options: -DGHCI
+  
+  if flag(disable-lens)
+    cpp-options: -DDISABLE_LENS
diff --git a/lib/rl_bindings.c b/lib/rl_bindings.c
--- a/lib/rl_bindings.c
+++ b/lib/rl_bindings.c
@@ -2382,10 +2382,8 @@
 
 void CustomCallback(int logLevel, const char *text, va_list args)
 {
-    size_t n = vsnprintf(NULL, 0, text, args) + 1;
-    char *buffer = malloc(n);
-    vsnprintf(buffer, n, text, args);
-    customCallback(logLevel, buffer);
+    char *formatted = TextFormat(text, args);
+    customCallback(logLevel, formatted);
 }
 
 RLBIND void SetTraceLogCallback_(TraceLogCallback_ a)
diff --git a/src/Raylib/Internal/Foreign.hs b/src/Raylib/Internal/Foreign.hs
--- a/src/Raylib/Internal/Foreign.hs
+++ b/src/Raylib/Internal/Foreign.hs
@@ -44,8 +44,13 @@
 import Foreign (FunPtr, Ptr, Storable (peek, peekByteOff, poke, sizeOf), allocaBytes, castPtr, malloc, newArray, nullPtr, peekArray, plusPtr, pokeArray0, with, withArray, withArrayLen)
 import Foreign.C (CFloat, CInt, CString, CUChar, CUInt, peekCString, withCString)
 import Foreign.C.Types (CBool, CChar, CShort, CUShort)
+
+#ifndef DISABLE_LENS
+
 import Linear (V2, V3, V4)
 
+#endif
+
 -- Internal utility functions
 
 #ifdef WEB_FFI
@@ -111,11 +116,15 @@
           let val = arr !! i in rlFreeDependents val (plusPtr ptr (i * sizeOf val))
       )
 
+#ifndef DISABLE_LENS
+
 instance Freeable (V2 a)
 
 instance Freeable (V3 a)
 
 instance Freeable (V4 a)
+
+#endif
 
 rlFreeMaybeArray :: (Freeable a, Storable a) => Maybe [a] -> Ptr a -> IO ()
 rlFreeMaybeArray Nothing _ = return ()
diff --git a/src/Raylib/Internal/TH.hs b/src/Raylib/Internal/TH.hs
--- a/src/Raylib/Internal/TH.hs
+++ b/src/Raylib/Internal/TH.hs
@@ -3,58 +3,32 @@
 {-# LANGUAGE TupleSections #-}
 
 -- | Template Haskell functions used internally
-module Raylib.Internal.TH (genLenses, genNative) where
-
-import Control.Lens (makeLensesFor)
-import Control.Monad (zipWithM)
+module Raylib.Internal.TH (genNative) where
 
 #ifdef WEB_FFI
 
 import Language.Haskell.TH
   ( Body (NormalB),
     Clause (Clause),
-    Con (RecC),
-    Dec (DataD, FunD, SigD),
+    Dec (FunD, SigD),
     DecsQ,
     Exp (AppE, LitE, VarE),
-    Info (TyConI),
     Lit (StringL),
-    Name,
     TypeQ,
     mkName,
-    nameBase,
-    reify,
   )
 import Raylib.Internal.Web.Native (callRaylibFunction)
 
 #else
 
 import Language.Haskell.TH
-  ( Con (RecC),
-    Dec (DataD, ForeignD),
+  ( Dec (ForeignD),
     DecsQ,
-    Info (TyConI),
-    Name,
     TypeQ,
-    mkName,
-    nameBase,
-    reify, Foreign (ImportF), Callconv (CCall), Safety (Safe),
+    mkName, Foreign (ImportF), Callconv (CCall), Safety (Safe),
   )
 
 #endif
-
--- | Creates lenses with an underscore before field names; e.g. @vector2'x@
---   becomes the lens @_vector2'x@
-genLenses :: [Name] -> DecsQ
-genLenses names = do
-  infos <- mapM reify names
-  concat <$> zipWithM genLensesForType names infos
-  where
-    genLensesForType name (TyConI (DataD _ _ _ _ [RecC _ ctors] _)) =
-      makeLensesFor mapping name
-      where
-        mapping = map (\(a, _, _) -> let fName = nameBase a in (fName, '_' : fName)) ctors
-    genLensesForType _ _ = error "(genLenses) Received a name that does not refer to a valid type!"
 
 -- | Generates native code for the given functions. On non-web platforms, this
 --   means @foreign import@ statements. On web platforms, this means
diff --git a/src/Raylib/Types/Core.hs b/src/Raylib/Types/Core.hs
--- a/src/Raylib/Types/Core.hs
+++ b/src/Raylib/Types/Core.hs
@@ -2,6 +2,7 @@
 {-# LANGUAGE PatternSynonyms #-}
 {-# LANGUAGE TypeSynonymInstances #-}
 {-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE CPP #-}
 
 -- | Bindings for types used in all raylib modules
 module Raylib.Types.Core
@@ -140,11 +141,16 @@
     newCString,
     peekCString,
   )
-import Linear (V2(V2), V3(V3), V4(V4))
 import Raylib.Internal (Closeable(..), _unloadAutomationEventList, addAutomationEventList)
 import Raylib.Internal.Foreign (Freeable (rlFreeDependents), c'free, peekStaticArray, pokeStaticArray)
 
+#ifndef DISABLE_LENS
 
+import Linear (V2(V2), V3(V3), V4(V4))
+
+#endif
+
+
 ---------------------------------------
 -- core enums -------------------------
 ---------------------------------------
@@ -659,25 +665,97 @@
 -- core structures --------------------
 ---------------------------------------
 
+pattern Vector2 :: Float -> Float -> Vector2
+pattern Vector3 :: Float -> Float -> Float -> Vector3
+pattern Vector4 :: Float -> Float -> Float -> Float -> Vector4
 
+#ifdef DISABLE_LENS
+
+data Vector2' = Vector2' Float Float deriving (Eq, Show, Freeable)
+instance Storable Vector2' where
+  sizeOf _ = 8
+  alignment _ = 4
+  peek _p = do
+    x <- realToFrac <$> peek (p'vector2'x _p)
+    y <- realToFrac <$> peek (p'vector2'y _p)
+    return $ Vector2' x y
+  poke _p (Vector2' x y) = do
+    poke (p'vector2'x _p) (realToFrac x)
+    poke (p'vector2'y _p) (realToFrac y)
+    return ()
+type Vector2 = Vector2'
+
+pattern Vector2
+  { vector2'x ,
+    vector2'y
+  } = Vector2' vector2'x vector2'y
+{-# COMPLETE Vector2 :: Vector2' #-}
+
+data Vector3' = Vector3' Float Float Float deriving (Eq, Show, Freeable)
+
+instance Storable Vector3' where
+  sizeOf _ = 12
+  alignment _ = 4
+  peek _p = do
+    x <- realToFrac <$> peek (p'vector3'x _p)
+    y <- realToFrac <$> peek (p'vector3'y _p)
+    z <- realToFrac <$> peek (p'vector3'z _p)
+    return $ Vector3' x y z
+  poke _p (Vector3' x y z) = do
+    poke (p'vector3'x _p) (realToFrac x)
+    poke (p'vector3'y _p) (realToFrac y)
+    poke (p'vector3'z _p) (realToFrac z)
+    return ()
+
+type Vector3 = Vector3'
+
+pattern Vector3
+  { vector3'x ,
+    vector3'y ,
+    vector3'z
+  } = Vector3' vector3'x vector3'y vector3'z
+{-# COMPLETE Vector3 :: Vector3' #-}
+
+data Vector4' = Vector4' Float Float Float Float deriving (Eq, Show, Freeable)
+
+instance Storable Vector4' where
+  sizeOf _ = 16
+  alignment _ = 4
+  peek _p = do
+    x <- realToFrac <$> peek (p'vector4'x _p)
+    y <- realToFrac <$> peek (p'vector4'y _p)
+    z <- realToFrac <$> peek (p'vector4'z _p)
+    w <- realToFrac <$> peek (p'vector4'w _p)
+    return $ Vector4' x y z w
+  poke _p (Vector4' x y z w) = do
+    poke (p'vector4'x _p) (realToFrac x)
+    poke (p'vector4'y _p) (realToFrac y)
+    poke (p'vector4'z _p) (realToFrac z)
+    poke (p'vector4'w _p) (realToFrac w)
+    return ()
+
+type Vector4 = Vector4'
+
+pattern Vector4
+  { vector4'x,
+    vector4'y,
+    vector4'z,
+    vector4'w
+  } = Vector4' vector4'x vector4'y vector4'z vector4'w
+{-# COMPLETE Vector4 :: Vector4' #-}
+
+#else
+
 type Vector2 = V2 Float
 
-pattern Vector2 :: Float -> Float -> Vector2
 pattern Vector2
   { vector2'x ,
     vector2'y
   } = V2 vector2'x vector2'y
 {-# COMPLETE Vector2 :: V2 #-}
 
-p'vector2'x :: Ptr Vector2 -> Ptr CFloat
-p'vector2'x = (`plusPtr` 0)
-
-p'vector2'y :: Ptr Vector2 -> Ptr CFloat
-p'vector2'y = (`plusPtr` 4)
-
 type Vector3 = V3 Float
 
-pattern Vector3 :: Float -> Float -> Float -> Vector3
 pattern Vector3
   { vector3'x ,
     vector3'y ,
@@ -685,18 +763,8 @@
   } = V3 vector3'x vector3'y vector3'z
 {-# COMPLETE Vector3 :: V3 #-}
 
-p'vector3'x :: Ptr Vector3 -> Ptr CFloat
-p'vector3'x = (`plusPtr` 0)
-
-p'vector3'y :: Ptr Vector3 -> Ptr CFloat
-p'vector3'y = (`plusPtr` 4)
-
-p'vector3'z :: Ptr Vector3 -> Ptr CFloat
-p'vector3'z = (`plusPtr` 8)
-
 type Vector4 = V4 Float
 
-pattern Vector4 :: a -> a -> a -> a -> V4 a
 pattern Vector4
   { vector4'x,
     vector4'y,
@@ -704,6 +772,23 @@
     vector4'w
   } = V4 vector4'x vector4'y vector4'z vector4'w
 {-# COMPLETE Vector4 :: V4 #-}
+
+#endif
+
+p'vector2'x :: Ptr Vector2 -> Ptr CFloat
+p'vector2'x = (`plusPtr` 0)
+
+p'vector2'y :: Ptr Vector2 -> Ptr CFloat
+p'vector2'y = (`plusPtr` 4)
+
+p'vector3'x :: Ptr Vector3 -> Ptr CFloat
+p'vector3'x = (`plusPtr` 0)
+
+p'vector3'y :: Ptr Vector3 -> Ptr CFloat
+p'vector3'y = (`plusPtr` 4)
+
+p'vector3'z :: Ptr Vector3 -> Ptr CFloat
+p'vector3'z = (`plusPtr` 8)
 
 vectorToColor :: Vector4 -> Color
 vectorToColor (Vector4 x y z w) = Color (round $ x * 255) (round $ y * 255) (round $ z * 255) (round $ w * 255)
diff --git a/src/Raylib/Util.hs b/src/Raylib/Util.hs
--- a/src/Raylib/Util.hs
+++ b/src/Raylib/Util.hs
@@ -35,7 +35,6 @@
 where
 
 import Control.Monad (void)
-import Control.Monad.Catch (MonadMask, bracket, bracket_)
 import Control.Monad.IO.Class (MonadIO (liftIO))
 import Raylib.Core (beginBlendMode, beginDrawing, beginMode2D, beginMode3D, beginScissorMode, beginShaderMode, beginTextureMode, beginVrStereoMode, closeWindow, endBlendMode, endDrawing, endMode2D, endMode3D, endScissorMode, endShaderMode, endTextureMode, endVrStereoMode, initWindow, setTargetFPS, windowShouldClose)
 import Raylib.Internal (WindowResources, Closeable (..), managed)
@@ -52,6 +51,7 @@
     VrStereoConfig,
   )
 import Raylib.Util.Math (Vector (vectorNormalize, (|-|)))
+import Control.Exception (bracket, bracket_)
 
 #ifdef WEB_FFI
 
@@ -66,10 +66,11 @@
 
 #endif
 
+
+
 -- | NOTE: Only for native targets. If your program is intended to
 --         run on the web, use `raylibApplication` instead.
 withWindow ::
-  (MonadIO m, MonadMask m) =>
   -- | Window width
   Int ->
   -- | Window height
@@ -78,32 +79,32 @@
   String ->
   -- | Target FPS
   Int ->
-  (WindowResources -> m b) ->
-  m b
+  (WindowResources -> IO b) ->
+  IO b
 withWindow w h title fps = bracket (liftIO $ initWindow w h title <* setTargetFPS fps) (liftIO . closeWindow . Just)
 
-drawing :: (MonadIO m, MonadMask m) => m b -> m b
+drawing :: IO b -> IO b
 drawing = bracket_ (liftIO beginDrawing) (liftIO endDrawing)
 
-mode2D :: (MonadIO m, MonadMask m) => Camera2D -> m b -> m b
+mode2D :: Camera2D -> IO b -> IO b
 mode2D camera = bracket_ (liftIO (beginMode2D camera)) (liftIO endMode2D)
 
-mode3D :: (MonadIO m, MonadMask m) => Camera3D -> m b -> m b
+mode3D :: Camera3D -> IO b -> IO b
 mode3D camera = bracket_ (liftIO (beginMode3D camera)) (liftIO endMode3D)
 
-textureMode :: (MonadIO m, MonadMask m) => RenderTexture -> m b -> m b
+textureMode :: RenderTexture -> IO b -> IO b
 textureMode rt = bracket_ (liftIO (beginTextureMode rt)) (liftIO endTextureMode)
 
-shaderMode :: (MonadIO m, MonadMask m) => Shader -> m b -> m b
+shaderMode :: Shader -> IO b -> IO b
 shaderMode shader = bracket_ (liftIO (beginShaderMode shader)) (liftIO endShaderMode)
 
-blendMode :: (MonadIO m, MonadMask m) => BlendMode -> m b -> m b
+blendMode :: BlendMode -> IO b -> IO b
 blendMode bm = bracket_ (liftIO (beginBlendMode bm)) (liftIO endBlendMode)
 
-scissorMode :: (MonadIO m, MonadMask m) => Int -> Int -> Int -> Int -> m b -> m b
+scissorMode :: Int -> Int -> Int -> Int -> IO b -> IO b
 scissorMode x y width height = bracket_ (liftIO (beginScissorMode x y width height)) (liftIO endScissorMode)
 
-vrStereoMode :: (MonadIO m, MonadMask m) => VrStereoConfig -> m b -> m b
+vrStereoMode :: VrStereoConfig -> IO b -> IO b
 vrStereoMode config = bracket_ (liftIO (beginVrStereoMode config)) (liftIO endVrStereoMode)
 
 -- | Gets the direction of a camera as a ray.
diff --git a/src/Raylib/Util/Lenses.hs b/src/Raylib/Util/Lenses.hs
--- a/src/Raylib/Util/Lenses.hs
+++ b/src/Raylib/Util/Lenses.hs
@@ -5,7 +5,7 @@
 module Raylib.Util.Lenses where
 
 import Control.Lens (Lens', lens)
-import Raylib.Internal.TH (genLenses)
+import Raylib.Util.Lenses.TH (genLenses)
 import qualified Raylib.Types as RL
 
 $( genLenses
diff --git a/src/Raylib/Util/Lenses/TH.hs b/src/Raylib/Util/Lenses/TH.hs
new file mode 100644
--- /dev/null
+++ b/src/Raylib/Util/Lenses/TH.hs
@@ -0,0 +1,26 @@
+module Raylib.Util.Lenses.TH (genLenses) where
+
+import Language.Haskell.TH
+  ( Con (RecC),
+    Dec (DataD),
+    DecsQ,
+    Name,
+    Info (TyConI),
+    nameBase,
+    reify
+  )
+import Control.Lens (makeLensesFor)
+import Control.Monad (zipWithM)
+
+-- | Creates lenses with an underscore before field names; e.g. @vector2'x@
+--   becomes the lens @_vector2'x@
+genLenses :: [Name] -> DecsQ
+genLenses names = do
+  infos <- mapM reify names
+  concat <$> zipWithM genLensesForType names infos
+  where
+    genLensesForType name (TyConI (DataD _ _ _ _ [RecC _ ctors] _)) =
+      makeLensesFor mapping name
+      where
+        mapping = map (\(a, _, _) -> let fName = nameBase a in (fName, '_' : fName)) ctors
+    genLensesForType _ _ = error "(genLenses) Received a name that does not refer to a valid type!"
