├── .gitignore ├── LiquidMetalShader.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── LiquidMetalShader ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── ContentView.swift ├── LiquidMetalShader.entitlements ├── LiquidMetalShaderApp.swift ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json └── liquid.metal ├── README.md └── window.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## Obj-C/Swift specific 9 | *.hmap 10 | 11 | ## App packaging 12 | *.ipa 13 | *.dSYM.zip 14 | *.dSYM 15 | 16 | ## Playgrounds 17 | timeline.xctimeline 18 | playground.xcworkspace 19 | 20 | # Swift Package Manager 21 | # 22 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 23 | # Packages/ 24 | # Package.pins 25 | # Package.resolved 26 | # *.xcodeproj 27 | # 28 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 29 | # hence it is not needed unless you have added a package configuration file to your project 30 | # .swiftpm 31 | 32 | .build/ 33 | 34 | # CocoaPods 35 | # 36 | # We recommend against adding the Pods directory to your .gitignore. However 37 | # you should judge for yourself, the pros and cons are mentioned at: 38 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 39 | # 40 | # Pods/ 41 | # 42 | # Add this line if you want to avoid checking in source code from the Xcode workspace 43 | # *.xcworkspace 44 | 45 | # Carthage 46 | # 47 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 48 | # Carthage/Checkouts 49 | 50 | Carthage/Build/ 51 | 52 | # fastlane 53 | # 54 | # It is recommended to not store the screenshots in the git repo. 55 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 56 | # For more information about the recommended setup visit: 57 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 58 | 59 | fastlane/report.xml 60 | fastlane/Preview.html 61 | fastlane/screenshots/**/*.png 62 | fastlane/test_output 63 | -------------------------------------------------------------------------------- /LiquidMetalShader.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 77; 7 | objects = { 8 | 9 | /* Begin PBXFileReference section */ 10 | 34BB40BE2D673B6500435AE3 /* LiquidMetalShader.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LiquidMetalShader.app; sourceTree = BUILT_PRODUCTS_DIR; }; 11 | /* End PBXFileReference section */ 12 | 13 | /* Begin PBXFileSystemSynchronizedRootGroup section */ 14 | 34BB40C02D673B6500435AE3 /* LiquidMetalShader */ = { 15 | isa = PBXFileSystemSynchronizedRootGroup; 16 | path = LiquidMetalShader; 17 | sourceTree = ""; 18 | }; 19 | /* End PBXFileSystemSynchronizedRootGroup section */ 20 | 21 | /* Begin PBXFrameworksBuildPhase section */ 22 | 34BB40BB2D673B6500435AE3 /* Frameworks */ = { 23 | isa = PBXFrameworksBuildPhase; 24 | buildActionMask = 2147483647; 25 | files = ( 26 | ); 27 | runOnlyForDeploymentPostprocessing = 0; 28 | }; 29 | /* End PBXFrameworksBuildPhase section */ 30 | 31 | /* Begin PBXGroup section */ 32 | 34BB40B52D673B6500435AE3 = { 33 | isa = PBXGroup; 34 | children = ( 35 | 34BB40C02D673B6500435AE3 /* LiquidMetalShader */, 36 | 34BB40BF2D673B6500435AE3 /* Products */, 37 | ); 38 | sourceTree = ""; 39 | }; 40 | 34BB40BF2D673B6500435AE3 /* Products */ = { 41 | isa = PBXGroup; 42 | children = ( 43 | 34BB40BE2D673B6500435AE3 /* LiquidMetalShader.app */, 44 | ); 45 | name = Products; 46 | sourceTree = ""; 47 | }; 48 | /* End PBXGroup section */ 49 | 50 | /* Begin PBXNativeTarget section */ 51 | 34BB40BD2D673B6500435AE3 /* LiquidMetalShader */ = { 52 | isa = PBXNativeTarget; 53 | buildConfigurationList = 34BB40CD2D673B6700435AE3 /* Build configuration list for PBXNativeTarget "LiquidMetalShader" */; 54 | buildPhases = ( 55 | 34BB40BA2D673B6500435AE3 /* Sources */, 56 | 34BB40BB2D673B6500435AE3 /* Frameworks */, 57 | 34BB40BC2D673B6500435AE3 /* Resources */, 58 | ); 59 | buildRules = ( 60 | ); 61 | dependencies = ( 62 | ); 63 | fileSystemSynchronizedGroups = ( 64 | 34BB40C02D673B6500435AE3 /* LiquidMetalShader */, 65 | ); 66 | name = LiquidMetalShader; 67 | packageProductDependencies = ( 68 | ); 69 | productName = LiquidMetalShader; 70 | productReference = 34BB40BE2D673B6500435AE3 /* LiquidMetalShader.app */; 71 | productType = "com.apple.product-type.application"; 72 | }; 73 | /* End PBXNativeTarget section */ 74 | 75 | /* Begin PBXProject section */ 76 | 34BB40B62D673B6500435AE3 /* Project object */ = { 77 | isa = PBXProject; 78 | attributes = { 79 | BuildIndependentTargetsInParallel = 1; 80 | LastSwiftUpdateCheck = 1620; 81 | LastUpgradeCheck = 1620; 82 | TargetAttributes = { 83 | 34BB40BD2D673B6500435AE3 = { 84 | CreatedOnToolsVersion = 16.2; 85 | }; 86 | }; 87 | }; 88 | buildConfigurationList = 34BB40B92D673B6500435AE3 /* Build configuration list for PBXProject "LiquidMetalShader" */; 89 | developmentRegion = en; 90 | hasScannedForEncodings = 0; 91 | knownRegions = ( 92 | en, 93 | Base, 94 | ); 95 | mainGroup = 34BB40B52D673B6500435AE3; 96 | minimizedProjectReferenceProxies = 1; 97 | preferredProjectObjectVersion = 77; 98 | productRefGroup = 34BB40BF2D673B6500435AE3 /* Products */; 99 | projectDirPath = ""; 100 | projectRoot = ""; 101 | targets = ( 102 | 34BB40BD2D673B6500435AE3 /* LiquidMetalShader */, 103 | ); 104 | }; 105 | /* End PBXProject section */ 106 | 107 | /* Begin PBXResourcesBuildPhase section */ 108 | 34BB40BC2D673B6500435AE3 /* Resources */ = { 109 | isa = PBXResourcesBuildPhase; 110 | buildActionMask = 2147483647; 111 | files = ( 112 | ); 113 | runOnlyForDeploymentPostprocessing = 0; 114 | }; 115 | /* End PBXResourcesBuildPhase section */ 116 | 117 | /* Begin PBXSourcesBuildPhase section */ 118 | 34BB40BA2D673B6500435AE3 /* Sources */ = { 119 | isa = PBXSourcesBuildPhase; 120 | buildActionMask = 2147483647; 121 | files = ( 122 | ); 123 | runOnlyForDeploymentPostprocessing = 0; 124 | }; 125 | /* End PBXSourcesBuildPhase section */ 126 | 127 | /* Begin XCBuildConfiguration section */ 128 | 34BB40CB2D673B6700435AE3 /* Debug */ = { 129 | isa = XCBuildConfiguration; 130 | buildSettings = { 131 | ALWAYS_SEARCH_USER_PATHS = NO; 132 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 133 | CLANG_ANALYZER_NONNULL = YES; 134 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 135 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 136 | CLANG_ENABLE_MODULES = YES; 137 | CLANG_ENABLE_OBJC_ARC = YES; 138 | CLANG_ENABLE_OBJC_WEAK = YES; 139 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 140 | CLANG_WARN_BOOL_CONVERSION = YES; 141 | CLANG_WARN_COMMA = YES; 142 | CLANG_WARN_CONSTANT_CONVERSION = YES; 143 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 144 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 145 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 146 | CLANG_WARN_EMPTY_BODY = YES; 147 | CLANG_WARN_ENUM_CONVERSION = YES; 148 | CLANG_WARN_INFINITE_RECURSION = YES; 149 | CLANG_WARN_INT_CONVERSION = YES; 150 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 151 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 152 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 153 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 154 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 155 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 156 | CLANG_WARN_STRICT_PROTOTYPES = YES; 157 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 158 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 159 | CLANG_WARN_UNREACHABLE_CODE = YES; 160 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 161 | COPY_PHASE_STRIP = NO; 162 | DEBUG_INFORMATION_FORMAT = dwarf; 163 | ENABLE_STRICT_OBJC_MSGSEND = YES; 164 | ENABLE_TESTABILITY = YES; 165 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 166 | GCC_C_LANGUAGE_STANDARD = gnu17; 167 | GCC_DYNAMIC_NO_PIC = NO; 168 | GCC_NO_COMMON_BLOCKS = YES; 169 | GCC_OPTIMIZATION_LEVEL = 0; 170 | GCC_PREPROCESSOR_DEFINITIONS = ( 171 | "DEBUG=1", 172 | "$(inherited)", 173 | ); 174 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 175 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 176 | GCC_WARN_UNDECLARED_SELECTOR = YES; 177 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 178 | GCC_WARN_UNUSED_FUNCTION = YES; 179 | GCC_WARN_UNUSED_VARIABLE = YES; 180 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 181 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 182 | MTL_FAST_MATH = YES; 183 | ONLY_ACTIVE_ARCH = YES; 184 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; 185 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 186 | }; 187 | name = Debug; 188 | }; 189 | 34BB40CC2D673B6700435AE3 /* Release */ = { 190 | isa = XCBuildConfiguration; 191 | buildSettings = { 192 | ALWAYS_SEARCH_USER_PATHS = NO; 193 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 194 | CLANG_ANALYZER_NONNULL = YES; 195 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 196 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 197 | CLANG_ENABLE_MODULES = YES; 198 | CLANG_ENABLE_OBJC_ARC = YES; 199 | CLANG_ENABLE_OBJC_WEAK = YES; 200 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 201 | CLANG_WARN_BOOL_CONVERSION = YES; 202 | CLANG_WARN_COMMA = YES; 203 | CLANG_WARN_CONSTANT_CONVERSION = YES; 204 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 205 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 206 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 207 | CLANG_WARN_EMPTY_BODY = YES; 208 | CLANG_WARN_ENUM_CONVERSION = YES; 209 | CLANG_WARN_INFINITE_RECURSION = YES; 210 | CLANG_WARN_INT_CONVERSION = YES; 211 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 212 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 213 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 214 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 215 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 216 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 217 | CLANG_WARN_STRICT_PROTOTYPES = YES; 218 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 219 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 220 | CLANG_WARN_UNREACHABLE_CODE = YES; 221 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 222 | COPY_PHASE_STRIP = NO; 223 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 224 | ENABLE_NS_ASSERTIONS = NO; 225 | ENABLE_STRICT_OBJC_MSGSEND = YES; 226 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 227 | GCC_C_LANGUAGE_STANDARD = gnu17; 228 | GCC_NO_COMMON_BLOCKS = YES; 229 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 230 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 231 | GCC_WARN_UNDECLARED_SELECTOR = YES; 232 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 233 | GCC_WARN_UNUSED_FUNCTION = YES; 234 | GCC_WARN_UNUSED_VARIABLE = YES; 235 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 236 | MTL_ENABLE_DEBUG_INFO = NO; 237 | MTL_FAST_MATH = YES; 238 | SWIFT_COMPILATION_MODE = wholemodule; 239 | }; 240 | name = Release; 241 | }; 242 | 34BB40CE2D673B6700435AE3 /* Debug */ = { 243 | isa = XCBuildConfiguration; 244 | buildSettings = { 245 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 246 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 247 | CODE_SIGN_ENTITLEMENTS = LiquidMetalShader/LiquidMetalShader.entitlements; 248 | CODE_SIGN_STYLE = Automatic; 249 | CURRENT_PROJECT_VERSION = 1; 250 | DEVELOPMENT_ASSET_PATHS = "\"LiquidMetalShader/Preview Content\""; 251 | DEVELOPMENT_TEAM = 9NSMA57MP7; 252 | ENABLE_HARDENED_RUNTIME = YES; 253 | ENABLE_PREVIEWS = YES; 254 | GENERATE_INFOPLIST_FILE = YES; 255 | "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; 256 | "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; 257 | "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; 258 | "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; 259 | "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; 260 | "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; 261 | "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; 262 | "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; 263 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 264 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 265 | IPHONEOS_DEPLOYMENT_TARGET = 18.2; 266 | LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; 267 | "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; 268 | MACOSX_DEPLOYMENT_TARGET = 15.2; 269 | MARKETING_VERSION = 1.0; 270 | PRODUCT_BUNDLE_IDENTIFIER = com.przemyslawbobak.LiquidMetalShader; 271 | PRODUCT_NAME = "$(TARGET_NAME)"; 272 | SDKROOT = auto; 273 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; 274 | SWIFT_EMIT_LOC_STRINGS = YES; 275 | SWIFT_VERSION = 5.0; 276 | TARGETED_DEVICE_FAMILY = "1,2,7"; 277 | XROS_DEPLOYMENT_TARGET = 2.2; 278 | }; 279 | name = Debug; 280 | }; 281 | 34BB40CF2D673B6700435AE3 /* Release */ = { 282 | isa = XCBuildConfiguration; 283 | buildSettings = { 284 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 285 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 286 | CODE_SIGN_ENTITLEMENTS = LiquidMetalShader/LiquidMetalShader.entitlements; 287 | CODE_SIGN_STYLE = Automatic; 288 | CURRENT_PROJECT_VERSION = 1; 289 | DEVELOPMENT_ASSET_PATHS = "\"LiquidMetalShader/Preview Content\""; 290 | DEVELOPMENT_TEAM = 9NSMA57MP7; 291 | ENABLE_HARDENED_RUNTIME = YES; 292 | ENABLE_PREVIEWS = YES; 293 | GENERATE_INFOPLIST_FILE = YES; 294 | "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; 295 | "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; 296 | "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; 297 | "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; 298 | "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; 299 | "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; 300 | "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; 301 | "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; 302 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 303 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 304 | IPHONEOS_DEPLOYMENT_TARGET = 18.2; 305 | LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; 306 | "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; 307 | MACOSX_DEPLOYMENT_TARGET = 15.2; 308 | MARKETING_VERSION = 1.0; 309 | PRODUCT_BUNDLE_IDENTIFIER = com.przemyslawbobak.LiquidMetalShader; 310 | PRODUCT_NAME = "$(TARGET_NAME)"; 311 | SDKROOT = auto; 312 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator"; 313 | SWIFT_EMIT_LOC_STRINGS = YES; 314 | SWIFT_VERSION = 5.0; 315 | TARGETED_DEVICE_FAMILY = "1,2,7"; 316 | XROS_DEPLOYMENT_TARGET = 2.2; 317 | }; 318 | name = Release; 319 | }; 320 | /* End XCBuildConfiguration section */ 321 | 322 | /* Begin XCConfigurationList section */ 323 | 34BB40B92D673B6500435AE3 /* Build configuration list for PBXProject "LiquidMetalShader" */ = { 324 | isa = XCConfigurationList; 325 | buildConfigurations = ( 326 | 34BB40CB2D673B6700435AE3 /* Debug */, 327 | 34BB40CC2D673B6700435AE3 /* Release */, 328 | ); 329 | defaultConfigurationIsVisible = 0; 330 | defaultConfigurationName = Release; 331 | }; 332 | 34BB40CD2D673B6700435AE3 /* Build configuration list for PBXNativeTarget "LiquidMetalShader" */ = { 333 | isa = XCConfigurationList; 334 | buildConfigurations = ( 335 | 34BB40CE2D673B6700435AE3 /* Debug */, 336 | 34BB40CF2D673B6700435AE3 /* Release */, 337 | ); 338 | defaultConfigurationIsVisible = 0; 339 | defaultConfigurationName = Release; 340 | }; 341 | /* End XCConfigurationList section */ 342 | }; 343 | rootObject = 34BB40B62D673B6500435AE3 /* Project object */; 344 | } 345 | -------------------------------------------------------------------------------- /LiquidMetalShader.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LiquidMetalShader/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /LiquidMetalShader/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "idiom" : "universal", 16 | "platform" : "ios", 17 | "size" : "1024x1024" 18 | }, 19 | { 20 | "appearances" : [ 21 | { 22 | "appearance" : "luminosity", 23 | "value" : "tinted" 24 | } 25 | ], 26 | "idiom" : "universal", 27 | "platform" : "ios", 28 | "size" : "1024x1024" 29 | }, 30 | { 31 | "idiom" : "mac", 32 | "scale" : "1x", 33 | "size" : "16x16" 34 | }, 35 | { 36 | "idiom" : "mac", 37 | "scale" : "2x", 38 | "size" : "16x16" 39 | }, 40 | { 41 | "idiom" : "mac", 42 | "scale" : "1x", 43 | "size" : "32x32" 44 | }, 45 | { 46 | "idiom" : "mac", 47 | "scale" : "2x", 48 | "size" : "32x32" 49 | }, 50 | { 51 | "idiom" : "mac", 52 | "scale" : "1x", 53 | "size" : "128x128" 54 | }, 55 | { 56 | "idiom" : "mac", 57 | "scale" : "2x", 58 | "size" : "128x128" 59 | }, 60 | { 61 | "idiom" : "mac", 62 | "scale" : "1x", 63 | "size" : "256x256" 64 | }, 65 | { 66 | "idiom" : "mac", 67 | "scale" : "2x", 68 | "size" : "256x256" 69 | }, 70 | { 71 | "idiom" : "mac", 72 | "scale" : "1x", 73 | "size" : "512x512" 74 | }, 75 | { 76 | "idiom" : "mac", 77 | "scale" : "2x", 78 | "size" : "512x512" 79 | } 80 | ], 81 | "info" : { 82 | "author" : "xcode", 83 | "version" : 1 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /LiquidMetalShader/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /LiquidMetalShader/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // LiquidMetalShader 4 | // 5 | // Created by Przemyslaw Bobak on 20/02/2025. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ContentView: View { 11 | 12 | struct ShaderConfiguration { 13 | var refraction: Float = 0.008 14 | var edge: Float = 0.8 15 | var liquid: Float = 0.7 16 | var patternBlur: Float = 0.005 17 | var patternScale: Float = 5.0 18 | var timeScale: Float = 0.200 19 | } 20 | 21 | @State var startDate = Date() 22 | @State var symbolName: String = "apple.logo" 23 | 24 | @State var configuration: ShaderConfiguration = ShaderConfiguration() 25 | 26 | var body: some View { 27 | TimelineView(.animation) { context in 28 | VStack { 29 | // Setup an SF Symbol image at a size 30 | Image(systemName: symbolName) 31 | .font(.system(size: 300)) 32 | 33 | // Apply the shader as a layer effect 34 | .layerEffect(ShaderLibrary.liquidMetal( 35 | .float(startDate.timeIntervalSinceNow), 36 | .float(configuration.refraction), 37 | .float(configuration.edge), 38 | .float(configuration.liquid), 39 | .float(configuration.patternBlur), 40 | .float(configuration.patternScale), 41 | .float(configuration.timeScale) 42 | ), maxSampleOffset: .zero) 43 | 44 | // Tap to reset the time interval 45 | .onTapGesture { 46 | startDate = Date() 47 | } 48 | 49 | Controls() 50 | } 51 | .padding() 52 | } 53 | } 54 | 55 | @ViewBuilder func Controls() -> some View { 56 | VStack { 57 | TextField("SF Symbol name", text: $symbolName) 58 | Slider(value: $configuration.refraction) { 59 | Text("Refraction: ") + Text(verbatim: String(format: "%.3f", configuration.refraction)) 60 | } 61 | Slider(value: $configuration.edge) { 62 | Text("Edge: ") + Text(verbatim: String(format: "%.3f", configuration.edge)) 63 | } 64 | Slider(value: $configuration.liquid) { 65 | Text("Liquid: ") + Text(verbatim: String(format: "%.3f", configuration.liquid)) 66 | } 67 | Slider(value: $configuration.patternBlur) { 68 | Text("Blur: ") + Text(verbatim: String(format: "%.3f", configuration.patternBlur)) 69 | } 70 | Slider(value: $configuration.patternScale, in: 1...10) { 71 | Text("Pattern Scale: ") + Text(verbatim: String(format: "%.3f", configuration.patternScale)) 72 | } 73 | Slider(value: $configuration.timeScale, in: 0...2) { 74 | Text("Time Scale") + Text(verbatim: String(format: "%.3f", configuration.timeScale)) 75 | } 76 | } 77 | } 78 | } 79 | 80 | #Preview { 81 | ContentView() 82 | } 83 | -------------------------------------------------------------------------------- /LiquidMetalShader/LiquidMetalShader.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /LiquidMetalShader/LiquidMetalShaderApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LiquidMetalShaderApp.swift 3 | // LiquidMetalShader 4 | // 5 | // Created by Przemyslaw Bobak on 20/02/2025. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct LiquidMetalShaderApp: App { 12 | var body: some Scene { 13 | WindowGroup { 14 | ContentView() 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /LiquidMetalShader/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /LiquidMetalShader/liquid.metal: -------------------------------------------------------------------------------- 1 | // 2 | // liquid.metal 3 | // LiquidMetalShader 4 | // 5 | // Created by Przemyslaw Bobak on 20/02/2025. 6 | // 7 | 8 | // [References] 9 | // Shader taken from: https://github.com/paper-design/liquid-logo/blob/main/src/app/hero/liquid-frag.ts 10 | // Posted on: https://x.com/sdothaney/status/1892286294107013506 11 | // Original Shader author: Stephen Haney / Paper Design 12 | 13 | #include 14 | #include 15 | using namespace metal; 16 | 17 | // Constants 18 | constant float PI = 3.14159265358979323846; 19 | constant float4 C = float4(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439); 20 | 21 | // Helper Functions 22 | float3 mod289(float3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } 23 | 24 | float2 mod289(float2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } 25 | 26 | float3 permute(float3 x) { return mod289(((x * 34.0) + 1.0) * x); } 27 | 28 | float snoise(float2 v) { 29 | float2 i = floor(v + dot(v, C.yy)); 30 | float2 x0 = v - i + dot(i, C.xx); 31 | float2 i1 = (x0.x > x0.y) ? float2(1.0, 0.0) : float2(0.0, 1.0); 32 | float4 x12 = x0.xyxy + C.xxzz; 33 | x12.xy -= i1; 34 | i = mod289(i); 35 | float3 p = permute(permute(i.y + float3(0.0, i1.y, 1.0)) + i.x + float3(0.0, i1.x, 1.0)); 36 | float3 m = max(0.5 - float3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0); 37 | m = m * m; 38 | m = m * m; 39 | float3 x = 2.0 * fract(p * C.www) - 1.0; 40 | float3 h = abs(x) - 0.5; 41 | float3 ox = floor(x + 0.5); 42 | float3 a0 = x - ox; 43 | m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h); 44 | float3 g; 45 | g.x = a0.x * x0.x + h.x * x0.y; 46 | g.yz = a0.yz * x12.xz + h.yz * x12.yw; 47 | return 130.0 * dot(m, g); 48 | } 49 | 50 | float2 rotate(float2 uv, float th) { 51 | float2x2 rotationMatrix = float2x2(cos(th), sin(th), -sin(th), cos(th)); 52 | return rotationMatrix * uv; 53 | } 54 | 55 | float get_color_channel(float c1, float c2, float stripe_p, float3 w, float extra_blur, float b, float u_patternBlur) { 56 | float ch = c2; 57 | float blur = u_patternBlur + extra_blur; 58 | ch = mix(ch, c1, smoothstep(0.0, blur, stripe_p)); 59 | float border = w[0]; 60 | ch = mix(ch, c2, smoothstep(border - blur, border + blur, stripe_p)); 61 | b = smoothstep(0.2, 0.8, b); 62 | border = w[0] + 0.4 * (1.0 - b) * w[1]; 63 | ch = mix(ch, c1, smoothstep(border - blur, border + blur, stripe_p)); 64 | border = w[0] + 0.5 * (1.0 - b) * w[1]; 65 | ch = mix(ch, c2, smoothstep(border - blur, border + blur, stripe_p)); 66 | border = w[0] + w[1]; 67 | ch = mix(ch, c1, smoothstep(border - blur, border + blur, stripe_p)); 68 | float gradient_t = (stripe_p - w[0] - w[1]) / w[2]; 69 | float gradient = mix(c1, c2, smoothstep(0.0, 1.0, gradient_t)); 70 | ch = mix(ch, gradient, smoothstep(border - blur, border + blur, stripe_p)); 71 | return ch; 72 | } 73 | 74 | float get_img_frame_alpha(float2 uv, float img_frame_width) { 75 | float img_frame_alpha = smoothstep(0.0, img_frame_width, uv.x) * smoothstep(1.0, 1.0 - img_frame_width, uv.x); 76 | img_frame_alpha *= smoothstep(0.0, img_frame_width, uv.y) * smoothstep(1.0, 1.0 - img_frame_width, uv.y); 77 | return img_frame_alpha; 78 | } 79 | 80 | // Main Function 81 | [[ stitchable ]] half4 liquidMetal(float2 position, 82 | SwiftUI::Layer layer, 83 | float u_time, 84 | float u_refraction, // Refraction strength 85 | float u_edge, // Edge sharpness 86 | float u_liquid, // Use strength to control liquid effect intensity 87 | float u_patternBlur, // Blur amount 88 | float u_patternScale, // Pattern scale, 89 | float u_timeScale // Time scale 90 | ) { 91 | float2 uv_raw = position / float2(layer.tex.get_width(), layer.tex.get_height()); 92 | float2 uv = uv_raw; 93 | half4 img = layer.sample(position); 94 | 95 | // If the original pixel is fully transparent, return transparent output immediately 96 | if (img.a == 0.0) { 97 | return img; 98 | } 99 | 100 | // Core shader logic 101 | float diagonal = uv.x - uv.y; 102 | float t = u_timeScale * u_time; 103 | 104 | float opacity = 1.0; 105 | float3 color1 = float3(0.98, 0.98, 1.0); 106 | float3 color2 = float3(0.1, 0.1, 0.1 + 0.1 * smoothstep(0.7, 1.3, uv.x + uv.y)); 107 | float edge = img.r; 108 | 109 | float2 grad_uv = uv - 0.5; 110 | float dist = length(grad_uv + float2(0.0, 0.2 * diagonal)); 111 | grad_uv = rotate(grad_uv, (0.25 - 0.2 * diagonal) * PI); 112 | 113 | float bulge = pow(1.8 * dist, 1.2); 114 | bulge = 1.0 - bulge; 115 | bulge *= pow(uv.y, 0.3); 116 | 117 | float cycle_width = u_patternScale; 118 | float thin_strip_1_ratio = 0.12 / cycle_width * (1.0 - 0.4 * bulge); 119 | float thin_strip_2_ratio = 0.07 / cycle_width * (1.0 + 0.4 * bulge); 120 | float wide_strip_ratio = 1.0 - thin_strip_1_ratio - thin_strip_2_ratio; 121 | float thin_strip_1_width = cycle_width * thin_strip_1_ratio; 122 | float thin_strip_2_width = cycle_width * thin_strip_2_ratio; 123 | 124 | opacity = 1.0 - smoothstep(0.9 - 0.5 * u_edge, 1.0 - 0.5 * u_edge, edge); 125 | opacity *= get_img_frame_alpha(uv_raw, 0.1); 126 | 127 | float noise = snoise(uv - float2(t, t)); 128 | edge += (1.0 - edge) * u_liquid * noise; 129 | 130 | float refr = clamp(1.0 - bulge, 0.0, 1.0); 131 | float dir = grad_uv.x + diagonal; 132 | dir -= 2.0 * noise * diagonal * (smoothstep(0.0, 1.0, edge) * smoothstep(1.0, 0.0, edge)); 133 | bulge *= clamp(pow(uv.y, 0.1), 0.3, 1.0); 134 | dir *= (0.1 + (1.1 - edge) * bulge); 135 | dir *= smoothstep(1.0, 0.7, edge); 136 | dir += 0.18 * (smoothstep(0.1, 0.2, uv.y) * smoothstep(0.4, 0.2, uv.y)); 137 | dir += 0.03 * (smoothstep(0.1, 0.2, 1.0 - uv.y) * smoothstep(0.4, 0.2, 1.0 - uv.y)); 138 | dir *= (0.5 + 0.5 * pow(uv.y, 2.0)); 139 | dir *= cycle_width; 140 | dir -= t; 141 | 142 | float refr_r = refr + 0.03 * bulge * noise; 143 | float refr_b = 1.3 * refr; 144 | refr_r += 5.0 * (smoothstep(-0.1, 0.2, uv.y) * smoothstep(0.5, 0.1, uv.y)) * 145 | (smoothstep(0.4, 0.6, bulge) * smoothstep(1.0, 0.4, bulge)); 146 | refr_r -= diagonal; 147 | refr_b += (smoothstep(0.0, 0.4, uv.y) * smoothstep(0.8, 0.1, uv.y)) * 148 | (smoothstep(0.4, 0.6, bulge) * smoothstep(0.8, 0.4, bulge)); 149 | refr_b -= 0.2 * edge; 150 | refr_r *= u_refraction; 151 | refr_b *= u_refraction; 152 | 153 | float3 w = float3(thin_strip_1_width, thin_strip_2_width, wide_strip_ratio); 154 | w[1] -= 0.02 * smoothstep(0.0, 1.0, edge + bulge); 155 | 156 | float stripe_r = fmod(dir + refr_r, 1.0); 157 | float r = get_color_channel(color1.r, color2.r, stripe_r, w, 158 | 0.02 + 0.03 * u_refraction * bulge, bulge, u_patternBlur); 159 | float stripe_g = fmod(dir, 1.0); 160 | float g = get_color_channel(color1.g, color2.g, stripe_g, w, 161 | 0.01 / (1.0 - diagonal), bulge, u_patternBlur); 162 | float stripe_b = fmod(dir - refr_b, 1.0); 163 | float b = get_color_channel(color1.b, color2.b, stripe_b, w, 164 | 0.01, bulge, u_patternBlur); 165 | 166 | return half4(r * opacity, g * opacity, b * opacity, opacity); 167 | } 168 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LiquidMetalShader 2 | 3 | Metal port of the liquid metal shader on https://liquid.paper.design 4 | 5 | Inspired by the [X post](https://x.com/sdothaney/status/1892286294107013506) by [@sdothaney](https://x.com/sdothaney). 6 | 7 | ## Features 8 | 9 | - Enter name of an SF Symbol for preview. 10 | - Customizable variables such as refraction, blur, time scale etc. 11 | - Tap to reset time interval (the entropy of the shader seems to reduce with time). 12 | 13 | ![screenshot](window.png) 14 | -------------------------------------------------------------------------------- /window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bobek-balinek/LiquidMetalShader/aa673a3fca63b66ee740b5c224297f2c9cbae14a/window.png --------------------------------------------------------------------------------