{"id":"y-workplace/dissolveeffect","name":"dissolveeffect","scope":"y-workplace","platform":"roblox","description":"DissolveEffect is a high-quality, pixel-based dissolve system for Roblox using EditableImage. Instead of simple transparency tweens, it renders dynamic dissolve and materialization effects with glowing edges, noise distortion, and smooth frame-based animation.","version":"1.0.4","latest":"1.0.4","versions":["1.0.2","1.0.4"],"license":"MIT","licenseRating":"safe","licenseCaveats":[],"licenseVerified":true,"dependencies":{"sleitnick/signal":{"version":"^2.0.3","alias":"Signal"}},"integrity":"34021135330e5357ca0afdca808b0909c1ae9b71fb0f1d58d0eb8f9dbbbb19d8","likes":0,"downloads":0,"install":"forest install y-workplace/dissolveeffect","url":"https://forest.dev/p/roblox/y-workplace/dissolveeffect","files":"https://api.forest.dev/ai/package/roblox/y-workplace/dissolveeffect/files","readme":"# Dissolve Effect\r\n\r\nHigh-performance dissolve / disintegration effect system for Roblox using `EditableImage`.\r\n\r\nDesigned for:\r\n\r\n* Characters\r\n* Meshes\r\n* UI Images\r\n* Decals / Textures\r\n* Large dissolve sequences\r\n* Reusable baked effects\r\n\r\nThe module supports multiple bake modes, glow edges, animated dissolve masks, cached frame generation, and reversible appear/disappear playback.\r\n\r\n---\r\n\r\n# Important\r\n\r\n## Client-side only\r\n\r\nThis module only works on the client.\r\n\r\n`EditableImage` does not exist on the server, so the module must be required from a `LocalScript` or from a `ModuleScript` used by a `LocalScript`.\r\n\r\n---\r\n\r\n## EditableImage must be enabled\r\n\r\nRoblox disables EditableImage APIs by default in published experiences.\r\n\r\nTo enable it:\r\n\r\n1. Verify your Roblox account (13+ and ID verified)\r\n2. Open `Game Settings`\r\n3. Go to `Security`\r\n4. Enable:\r\n\r\n```text\r\nAllow Mesh / Image APIs\r\n```\r\n\r\nIf this setting is disabled:\r\n\r\n* Baking will fail\r\n* EditableImage creation may fail\r\n* The dissolve effect may appear invisible or broken\r\n\r\nThis is a Roblox platform restriction.\r\n\r\n---\r\n\r\n# Recommended Folder Structure\r\n\r\n```text\r\nReplicatedStorage\r\n└── Packages\r\n    └── DissolveEffect\r\n        ├── Signal\r\n        ├── JobManager\r\n        ├── RuntimeWorker\r\n        ├── Actor\r\n        └── Mask\r\n```\r\n\r\nExample require:\r\n\r\n```luau\r\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\r\n\r\nlocal Dissolve = require(\r\n    ReplicatedStorage.Packages.DissolveEffect\r\n)\r\n```\r\n\r\n---\r\n\r\n# Bloom / Lighting\r\n\r\nThe glow and edge colors look significantly better when Bloom is enabled.\r\n\r\nAdd a Bloom instance (BloomEffect) to \"Lighting\" with the following properties:\r\n\r\n```luau\r\nIntensity = 3\r\nSize = 100\r\nThreshold = 3.5\r\n```\r\n\r\nUsing the Command Bar:\r\n\r\n```luau\r\nlocal Lighting = game:GetService(\"Lighting\")\r\nlocal bloom = Instance.new(\"BloomEffect\")\r\n\r\nbloom.Intensity = 3\r\nbloom.Size = 100\r\nbloom.Threshold = 3.5\r\n\r\nbloom.Parent = Lighting\r\n```\r\n\r\n---\r\n\r\n# How It Works\r\n\r\n## 1. Create the effect\r\n\r\nYou create a dissolve object using:\r\n\r\n```luau\r\nlocal dissolve = Dissolve.new({ object }, config)\r\n```\r\n\r\nThe module scans supported objects and prepares internal image data.\r\n\r\nThis preparation phase is called:\r\n\r\n```text\r\nBaking\r\n```\r\n\r\n---\r\n\r\n## 2. Wait until ready\r\n\r\nWhile preparing:\r\n\r\n```text\r\nState = \"Baking\"\r\n```\r\n\r\nAfter finishing:\r\n\r\n```text\r\nState = \"Ready\"\r\n```\r\n\r\nYou can:\r\n\r\n* Use `Ready`\r\n* Use `StateChanged`\r\n* Poll `IsReady()`\r\n\r\n---\r\n\r\n## 3. Start animation\r\n\r\n```luau\r\n:dissolve:Start()\r\n```\r\n\r\nThe original object becomes hidden.\r\n\r\nGenerated dissolve masks animate frame-by-frame until the effect completes.\r\n\r\n---\r\n\r\n## 4. Reverse playback\r\n\r\n```luau\r\n:dissolve:Start(true)\r\n```\r\n\r\nPassing `true` plays the animation backwards.\r\n\r\nInstead of dissolving away, the object reconstructs itself.\r\n\r\n---\r\n\r\n## 5. Cleanup\r\n\r\n```luau\r\n:dissolve:Destroy()\r\n```\r\n\r\nThis removes:\r\n\r\n* EditableImages\r\n* Masks\r\n* Signals\r\n* Cached references\r\n* Running jobs\r\n\r\nAlways destroy effects you no longer use.\r\n\r\n---\r\n\r\n# Public API\r\n\r\n```luau\r\nDissolve.new({ instances }, config?)\r\n\r\n:dissolve:Start(appear?)\r\n:dissolve:Reset()\r\n:dissolve:Destroy()\r\n:dissolve:SetConfig(config)\r\n\r\n:dissolve:IsReady()\r\n:dissolve:IsRunning()\r\n\r\n:dissolve:GetState()\r\n:dissolve:GetProgress()\r\n:dissolve:GetBakeProgress()\r\n```\r\n\r\n---\r\n\r\n# Signals\r\n\r\n```luau\r\n:dissolve.Ready\r\n:dissolve.StateChanged\r\n:dissolve.Completed\r\n```\r\n\r\n---\r\n\r\n# Lifecycle States\r\n\r\n| State       | Description                   |\r\n| ----------- | ----------------------------- |\r\n| `Idle`      | Created but not prepared yet  |\r\n| `Baking`    | Preparing dissolve frame data |\r\n| `Ready`     | Safe to start                 |\r\n| `Running`   | Animation currently playing   |\r\n| `Destroyed` | Effect no longer usable       |\r\n\r\n---\r\n\r\n# Supported Types\r\n\r\n## 3D\r\n\r\n* Part\r\n* MeshPart\r\n* Decal\r\n* Texture\r\n* SurfaceAppearance (`ColorMap only`)\r\n\r\n## UI\r\n\r\n* ImageLabel\r\n* ImageButton\r\n\r\n---\r\n\r\n# Unsupported Types\r\n\r\n```text\r\nMaterial\r\nMaterialVariant\r\n```\r\n\r\nThese are blocked by Roblox security restrictions.\r\n\r\n---\r\n\r\n# Asset Permission Notes\r\n\r\nEditableImage can only load textures that Roblox allows the experience to access.\r\n\r\nIf a texture is unavailable:\r\n\r\n* The module may fallback to solid colors\r\n* Some effects may appear simplified\r\n\r\n---\r\n\r\n# Quick Start\r\n\r\n## Minimal Example\r\n\r\n```luau\r\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\r\n\r\nlocal Dissolve = require(\r\n    ReplicatedStorage.Packages.DissolveEffect\r\n)\r\n\r\nlocal part = workspace.Part\r\n\r\nlocal dissolve = Dissolve.new({ part }, {\r\n    Bake = \"Mask\",\r\n})\r\n\r\nif dissolve:IsReady() then\r\n    dissolve:Start()\r\nelse\r\n    local connection\r\n\r\n    connection = dissolve.Ready:Connect(function()\r\n        connection:Disconnect()\r\n        dissolve:Start()\r\n    end)\r\nend\r\n```\r\n\r\n---\r\n\r\n# Examples\r\n\r\n# Helper Function\r\n\r\nUsed in multiple examples below.\r\n\r\n```luau\r\nlocal function startWhenReady(dissolve, appear: boolean?)\r\n    if dissolve:IsReady() then\r\n        dissolve:Start(appear)\r\n        return\r\n    end\r\n\r\n    local connection\r\n\r\n    connection = dissolve.Ready:Connect(function()\r\n        connection:Disconnect()\r\n        dissolve:Start(appear)\r\n    end)\r\nend\r\n```\r\n\r\n---\r\n\r\n## Dissolve a Character\r\n\r\n```luau\r\nlocal Players = game:GetService(\"Players\")\r\n\r\nlocal function dissolveCharacter(character: Model)\r\n    local dissolve = Dissolve.new({ character }, {\r\n        Speed = 0.2,\r\n        Bake = \"Mask\",\r\n\r\n        Behavior = {\r\n            AutoDiscover = \"All\",\r\n        },\r\n    })\r\n\r\n    startWhenReady(dissolve)\r\n\r\n    dissolve.Completed:Connect(function()\r\n        dissolve:Destroy()\r\n        character:Destroy()\r\n    end)\r\nend\r\n\r\nPlayers.LocalPlayer.CharacterAdded:Connect(function(character)\r\n    task.delay(3, function()\r\n        dissolveCharacter(character)\r\n    end)\r\nend)\r\n```\r\n\r\n---\r\n\r\n## Dissolve UI Images\r\n\r\n```luau\r\nlocal player = game:GetService(\"Players\").LocalPlayer\r\n\r\nlocal imageLabel = player.PlayerGui.ScreenGui.ImageLabel\r\n\r\nlocal dissolve = Dissolve.new({ imageLabel }, {\r\n    Speed = 0.25,\r\n    Bake = \"Mask\",\r\n})\r\n\r\nstartWhenReady(dissolve)\r\n```\r\n\r\n---\r\n\r\n## Reverse Playback (Appear)\r\n\r\n```luau\r\nlocal dissolve = Dissolve.new({ workspace.Part }, {\r\n    Bake = \"Mask\",\r\n})\r\n\r\nstartWhenReady(dissolve, true)\r\n```\r\n\r\n---\r\n\r\n## Polling Instead of Signals\r\n\r\n```luau\r\nlocal dissolve = Dissolve.new({ workspace.Part }, {\r\n    Bake = \"Full\",\r\n})\r\n\r\nwhile not dissolve:IsReady() do\r\n    task.wait()\r\nend\r\n\r\n:dissolve:Start()\r\n\r\nwhile dissolve:IsRunning() do\r\n    task.wait()\r\nend\r\n```\r\n\r\n---\r\n\r\n## Progress Tracking\r\n\r\n```luau\r\nRunService.RenderStepped:Connect(function()\r\n    print(dissolve:GetProgress())\r\n    print(dissolve:GetBakeProgress())\r\nend)\r\n```\r\n\r\n---\r\n\r\n# Bake Modes\r\n\r\n## Full\r\n\r\nFully pre-renders all frames.\r\n\r\n### Advantages\r\n\r\n* Fastest playback\r\n* Lowest runtime cost\r\n\r\n### Disadvantages\r\n\r\n* Highest memory usage\r\n* Longer preparation time\r\n\r\nBest for:\r\n\r\n* Small objects\r\n* Cinematics\r\n* Repeated effects\r\n\r\n---\r\n\r\n## Mask\r\n\r\nPrecomputes dissolve masks only.\r\n\r\n### Advantages\r\n\r\n* Lower memory usage\r\n* Good performance balance\r\n* Recommended mode\r\n\r\n### Disadvantages\r\n\r\n* Slightly more runtime processing\r\n\r\nBest for:\r\n\r\n* General gameplay\r\n* Characters\r\n* Most projects\r\n\r\n---\r\n\r\n## None\r\n\r\nNo baking.\r\n\r\nFrames are generated in real time.\r\n\r\n### Advantages\r\n\r\n* Instant startup\r\n* Minimal preparation wait\r\n\r\n### Disadvantages\r\n\r\n* Highest runtime CPU cost\r\n\r\nBest for:\r\n\r\n* Quick previews\r\n* Development tools\r\n* Editor workflows\r\n\r\n---\r\n\r\n# Yield Modes\r\n\r\nControls how aggressively baking work yields.\r\n\r\n| Mode         | Description                          |\r\n| ------------ | ------------------------------------ |\r\n| `Aggressive` | Fastest bake, may freeze frames more |\r\n| `Balanced`   | Recommended balance                  |\r\n| `Relaxed`    | Smoothest gameplay during baking     |\r\n\r\n---\r\n\r\n# AutoDiscover\r\n\r\nControls descendant scanning.\r\n\r\n| Mode       | Description               |\r\n| ---------- | ------------------------- |\r\n| `All`      | Recursive descendant scan |\r\n| `Children` | Direct children only      |\r\n| `None`     | Exact instances only      |\r\n\r\n---\r\n\r\n# Visual Parameters\r\n\r\n| Setting                      | Description                     |\r\n| ---------------------------- | ------------------------------- |\r\n| `Color`                      | Edge highlight color            |\r\n| `GlowColor`                  | Glow behind edges               |\r\n| `Speed`                      | Animation speed                 |\r\n| `EdgeWidth`                  | Hard edge thickness             |\r\n| `GlowWidth`                  | Glow size                       |\r\n| `NoiseScale`                 | Dissolve pattern frequency      |\r\n| `NoiseResolution`            | Internal noise texture size     |\r\n| `BakeFrames`                 | Amount of baked frames          |\r\n| `Size`                       | EditableImage resolution        |\r\n| `RegionFrequency`            | Dissolve band count             |\r\n| `ThicknessGain`              | Edge amplification              |\r\n| `ThicknessBias`              | Edge threshold offset           |\r\n| `WarpStrength`               | Boundary distortion amount      |\r\n| `NoiseMap`                   | Custom dissolve texture         |\r\n| `TransformAutoUpdateEnabled` | Updates moving targets          |\r\n| `EmissiveStrength`           | SurfaceAppearance glow strength |\r\n\r\n---\r\n\r\n# Preset Example\r\n\r\n```luau\r\nlocal dissolve = Dissolve.new({ workspace.Part }, {\r\n    Color = Color3.fromRGB(0, 255, 255),\r\n    GlowColor = Color3.fromRGB(0, 120, 255),\r\n\r\n    Speed = 0.15,\r\n\r\n    EdgeWidth = 1,\r\n    GlowWidth = 2,\r\n\r\n    NoiseScale = 0.5,\r\n    NoiseResolution = 64,\r\n\r\n    BakeFrames = 60,\r\n    Size = Vector2.new(128, 128),\r\n\r\n    WarpStrength = 0.5,\r\n\r\n    Bake = \"Mask\",\r\n    YieldMode = \"Balanced\",\r\n\r\n    Behavior = {\r\n        AutoDiscover = \"All\",\r\n    },\r\n})\r\n```\r\n\r\n---\r\n\r\n# Internal Cache Reuse\r\n\r\nThe module internally reuses baked frame data when:\r\n\r\n* The same textures are used\r\n* The same configuration is used\r\n\r\nThis makes repeated dissolves significantly cheaper after the first bake.\r\n\r\nUseful for:\r\n\r\n* Enemy waves\r\n* Rhythm gameplay\r\n* Repeated VFX\r\n* Character respawns\r\n\r\n---\r\n\r\n# Best Practices\r\n\r\n## Recommended Defaults\r\n\r\n```luau\r\nBake = \"Mask\"\r\nYieldMode = \"Balanced\"\r\nBakeFrames = 60\r\nSize = Vector2.new(128, 128)\r\n```\r\n\r\n---\r\n\r\n## Large Objects\r\n\r\nFor large meshes or characters:\r\n\r\n```luau\r\nYieldMode = \"Relaxed\"\r\n```\r\n\r\nThis reduces frame spikes while baking.\r\n\r\n---\r\n\r\n## UI Effects\r\n\r\nAvoid simultaneously animating:\r\n\r\n```luau\r\nImageTransparency\r\n```\r\n\r\nduring dissolve playback.\r\n\r\n---\r\n\r\n## Always Destroy Effects\r\n\r\n```luau\r\ndissolve:Destroy()\r\n```\r\n\r\nDo not leave unused effects alive.\r\n\r\n---\r\n\r\n# Installation\r\n## Roblox Creator Store\r\nAdd the module directly from the Creator Store:\r\n\r\n```\r\nCreator Store Model:\r\nhttps://create.roblox.com/store/asset/126151482111558\r\n```\r\n\r\n---\r\n\r\n## Wally\r\n\r\n```\r\n[dependencies]\r\nDissolveEffect = \"y-workplace/dissolveeffect@1.0.4\"\r\n```\r\n\r\nWally Package:\r\n\r\n```\r\nhttps://wally.run/package/y-workplace/dissolveeffect\r\n```\r\n\r\n---\r\n\r\n# License\r\n\r\nMIT License.\r\n","readmeTruncated":false}