Hello everyone,
I wanted to share something I made using the Editable Image API in an optimized way. If you’re looking to learn how to use it efficiently or just need some useful code, this might help. I used Perlin noise to generate smooth patterns and took advantage of the new buffer class for better performance. Everything runs at a stable 30 FPS without causing performance issues, so it works smoothly on any machine.
I made It very quickly (~15min) after seing this shader made in unity so if you find any problem I’d be happy to receive feedbacks.
I’ll post the source code below so you can check it out, learn from it, or use it in your own projects. Let me know if you have any thoughts or questions!
Code here:
Expend Me!
--[[
Created by:
@Ph0enixx_31 (TR-MZ) on 2025-11-02
Description:
This script creates a shader like noise effect on an image using the buffer module and the Editable Image API.
Note:
I'm sure this can be optimized and made better, but this is just a simple example of how you can use the buffer module and the Editable Image API to create a shader like effect on an image.
You are free to use this code in any way you want, but it would be nice if you could credit me if you use it in a project or something.
My patreon: https://www.patreon.com/trmz
My X: https://x.com/tr_mz_
]]
----[[ Services ]]-----------------------------------------------------------------------------
local AssetService = game:GetService("AssetService")
----[[ Constants ]]----------------------------------------------------------------------------
local SEED = math.random(math.random(1, 10^8))
local DURATION = 2
----[[ Variables ]]----------------------------------------------------------------------------
local editableImage
local imageBuffer
local noiseBuffer
local imageSize
local finished = false
----[[ Functions ]]----------------------------------------------------------------------------
local function createNoiseBuffer(noiseBuffer: buffer)
for i = 0, imageSize.X * imageSize.Y - 1 do
x = i % imageSize.X
y = math.floor(i / imageSize.X)
local noise = math.clamp(math.noise(x / 50, y / 50, SEED) , -1, 1)
noise += 1
noise = noise * 127
noise = math.floor(noise)
buffer.writeu8(noiseBuffer, i ,noise )
end
end
local function frame(frameIndex, imageBuffer: buffer, pixelsCount, noiseBuffer: buffer)
frameIndex = frameIndex * 254
for i = 0, pixelsCount - 1 do
local noiseValue = buffer.readu8(noiseBuffer, i)
if noiseValue > frameIndex then continue end
local pixelIndex = (i) * 4
-- You can also write a custom image here instead of setting the pixel color by using an other image buffer and taking its pixel color at index i
buffer.writeu8(imageBuffer, pixelIndex, 0)
--buffer.writeu8(imageBuffer, pixelIndex+1, 0)
--buffer.writeu8(imageBuffer, pixelIndex+2, 0)
buffer.writeu8(imageBuffer, pixelIndex+3, 255)
buffer.writeu8(noiseBuffer ,i, 255) -- this marks the pixel as already done so it doesn't recalculate it every frame
end
editableImage:WritePixelsBuffer(Vector2.zero, editableImage.Size, imageBuffer)
end
local function init()
editableImage = AssetService:CreateEditableImage({Size = Vector2.new(1024, 1024)}) -- You can use a custom image here using CreateEditableImageAsync
imageBuffer = editableImage:ReadPixelsBuffer(Vector2.new(0, 0), editableImage.Size)
noiseBuffer = buffer.create(editableImage.Size.X * editableImage.Size.Y)
script.Parent.ImageLabel.ImageContent = Content.fromObject(editableImage)
imageSize = editableImage.Size
createNoiseBuffer(noiseBuffer)
local startTime = os.clock()
while os.clock() - startTime < DURATION do
wait()
local progress = (os.clock() - startTime) / DURATION
frame(progress, imageBuffer, imageSize.X * imageSize.Y, noiseBuffer)
end
end
init()