Play Gifs on Guis

You may have seen this post when searching for ways to play Gifs in Roblox.

However, this is limited to workspace parts. You are unable to use screen or billboard guis with this method (To my knowledge). I’ve made a function to solve that.

This function should compensate for Roblox’s 1024 x 1024 image size limit too.

This size limitation greatly decreases the quality of the final result. Other than using Roblox’s Pixelated Resample Mode, there isn’t a convenient way to fix this. (To my knowledge)

Result: https://gyazo.com/c0e95220b912391b72315d0e0de09dfd

Example Gui
GifGui.rbxm (3.7 KB)

Spritesheet Example

Parameters:

ImageLabel: ImageLabel Instance
ImageWidth: Width in pixels of the spritesheet
ImageHeight: Height in pixels of the spritesheet
Rows: Number of rows on the spritesheet
Columns: Number of columns on the spritesheet
NumberOfFrames: Total number of frames or sections on the spritesheet
(Optional) ImageId: Image Id after uploaded to Roblox
(Optional) FPS: Frames per second to play

Function

local RobloxMaxImageSize = 1024
local function AnimateGif(ImageLabel:ImageLabel,ImageWidth,ImageHeight,Rows,Columns,NumberOfFrames,ImageId,FPS)
	
	if ImageId then ImageLabel.Image = "http://www.roblox.com/asset/?id=" .. ImageId end
	
	local RealWidth, RealHeight

	if math.max(ImageWidth,ImageHeight) > RobloxMaxImageSize then -- Compensate roblox size

		local Longest = ImageWidth > ImageHeight and "Width" or "Height"

		if Longest == "Width" then

			RealWidth = RobloxMaxImageSize
			RealHeight = (RealWidth / ImageWidth) * ImageHeight

		elseif Longest == "Height" then

			RealHeight = RobloxMaxImageSize
			RealWidth = (RealHeight / ImageHeight) * ImageWidth

		end

	else
		RealWidth,RealHeight = ImageWidth,ImageHeight
	end

	local FrameSize = Vector2.new(RealWidth/Columns,RealHeight/Rows)
	ImageLabel.ImageRectSize = FrameSize

	local CurrentRow, CurrentColumn = 0,0
	local Offsets = {}

	for i = 1,NumberOfFrames do

		local CurrentX = CurrentColumn * FrameSize.X
		local CurrentY = CurrentRow * FrameSize.Y

		table.insert(Offsets,Vector2.new(CurrentX,CurrentY))

		CurrentColumn += 1

		if CurrentColumn >= Columns then
			CurrentColumn = 0
			CurrentRow += 1
		end

	end
	
	local TimeInterval = FPS and 1/FPS or 0.1
	
	local Index = 0
	task.spawn(function()

		while task.wait(TimeInterval) and ImageLabel:IsDescendantOf(game) do
			Index += 1

			ImageLabel.ImageRectOffset = Offsets[Index]

			if Index >= NumberOfFrames then
				Index = 0
			end

		end

	end)

end

Steps

  1. Convert your gif to a spritesheet using any ‘gif to spritesheet’ converter that can be found online. I used this. There is an example of a spritesheet above.
  1. Upload your spritesheet to roblox. A method to do this is shown below.

    In the example above, I’ve copied the image id. We’ll need that id for later.

The next few steps varies on where your ImageLabel is located. But you need a script or local script to be able to access your ImageLabel. Use a script if your Gui is in the workspace, and use a local script if your Gui is a screen gui.

This is what I’m working with for this tutorial:
image

  1. In your script, paste the function. It should look something like this:
  1. Call the function and insert in your parameters. The description of each parameter is stated above. The image below is an example of calling it. (To make it more understandable, I’m using ‘parameter’ as a replacement of ‘arguments’. I understand that they are not the same thing.)
    image

That should be it. Once your parameters are entered in, your gif should play correctly. There is an example .rbmx file above that you can refer to.

54 Likes

Hey, thanks for the tutorial. It’s really useful and informative. And this is definitely a good use of making gifs on Roblox instead of having multiple decals and rounding that up to play which is not very performant and takes a very long time!

1 Like

Why?

1 Like

Can you please link me the GIF you used? Maybe I missed something in my code, or maybe it was the values inputted.

You could just use a circle image and tween the rotation?

3 Likes

I used the rbxassetid://7985282257 and I fix this

11664 width, 144 height. You confused rows and colums.
Script calculate 12.64… and I replace it to 12

local FPSs = 81 / 3.4

local RobloxMaxImageSize = 1024
local function AnimateGif(ImageLabel:ImageLabel,ImageWidth,ImageHeight,Rows,Columns,NumberOfFrames,ImageId,FPS)
	local NoFs = NumberOfFrames * 12
	
	
	if ImageId then ImageLabel.Image = "http://www.roblox.com/asset/?id=" .. ImageId end
	
	local RealWidth, RealHeight

	if math.max(ImageWidth,ImageHeight) > RobloxMaxImageSize then -- Compensate roblox size

		local Longest = ImageWidth > ImageHeight and "Width" or "Height"

		if Longest == "Width" then

			RealWidth = RobloxMaxImageSize
			RealHeight = (RealWidth / ImageWidth) * ImageHeight

		elseif Longest == "Height" then

			RealHeight = RobloxMaxImageSize
			RealWidth = (RealHeight / ImageHeight) * ImageWidth

		end

	else
		RealWidth,RealHeight = ImageWidth,ImageHeight
	end

	local FrameSize = Vector2.new(RealWidth/Columns,RealHeight/Rows)
	ImageLabel.ImageRectSize = Vector2.new(12,12)

	local CurrentRow, CurrentColumn = 0,0
	local Offsets = {}

	for i = 1,NumberOfFrames do

		local CurrentX = CurrentColumn * FrameSize.X
		local CurrentY = CurrentRow * FrameSize.Y

		table.insert(Offsets,Vector2.new(CurrentX,CurrentY))

		CurrentColumn += 1

		if CurrentColumn >= Columns then
			CurrentColumn = 0
			CurrentRow += 1
		end

	end
	
	local TimeInterval = FPS and 1/FPS or 0.1
	
	local Index = 0
	task.spawn(function()

		while task.wait(TimeInterval) and ImageLabel:IsDescendantOf(game) do
			while true do
				wait(TimeInterval)
			
			Index += 12

			ImageLabel.ImageRectOffset = Vector2.new(Index,0)

				if Index >= NoFs then
				ImageLabel.ImageRectOffset = Vector2.new(0,0)
				Index = 0
			end
end
		end

	end)

end

AnimateGif(script.Parent,11664,144,1,81,81,7985282257, FPSs)

The rows and columns are correct.
image

What calculated 12.64?

A possible reason is because of roblox’s rounding error.
Each frame is only 5 pixels tall and a little larger than 5 pixels wide (After roblox’s limitations). Roblox might have rounded it down to 5 by 5 pixels, causing this to happen.

I suggest you use a 9 by 9 grid for the spritesheet instead of a 1 by 81, the quality would be better and this error should be fixed.

11664 width, 144 height.

11664/144=81 frames
Rows - 81
Colums - 1

Thanks for advice! It’s worked!

1 Like

Rows are like the rows of pews in churches. They run from left to right. One pew / chair is one row.

Columns are like the columns of buildings. They run from top to bottom.

vIKQI-1

In your case, you have a very long spritesheet. This means that you have 1 very long row. Within your row, you have sections of your gifs, these are split up into columns.

I’m sorry if I seem petty, I didn’t mean it like that.

1 Like

Ah yes. Best use of it.
Don't click on this text - Roblox

7 Likes

spritesheet animator might helpful to tune gif settings.

why is your example completely broken

2023 08 03 11 39 02 - YouTube

I’ve just tested it out, it still works on my end tho. Is the example you’re using a fresh copy?

I did alter it a bit, but not too much. I mainly cleaned up the code, but should give the same result:

--[=[
	The parent of all classes.

	@class Sprite
]=]
local Sprite = { }
local RobloxMaxImageSize = 1024

--[=[
	Animates the given sprite to play like a GIF.

	@param image ImageLabel -- The image label that the sprite should be animated on
    @param frameSize Vector2 -- The size of each individual frame
    @param frames Vector2 -- The amount of frames on both the X and Y axis
    @param fps number? -- The amount of frames per second that the sprite should be played at. Defaults to 30
    @param imageId string? -- The image id that the image label should be set to, defaults to the initial image of the image label
]=]
function Sprite.Animate(image: ImageLabel, imageSize: Vector2, frames: Vector2, frameNumber: number, fps: number?, imageId: string?)
	fps = fps or 30
	imageId = imageId or image.Image

    image:SetAttribute("IsAnimationActive", true)

    if imageId then
        image.Image = imageId
    end
	
	local RealWidth, RealHeight
    local ImageWidth, ImageHeight = imageSize.X, imageSize.Y

	if math.max(ImageWidth, ImageHeight) > RobloxMaxImageSize then -- Compensate roblox size
		local Longest = ImageWidth > ImageHeight and "Width" or "Height"

		if Longest == "Width" then
			RealWidth = RobloxMaxImageSize
			RealHeight = (RealWidth / ImageWidth) * ImageHeight
		elseif Longest == "Height" then
			RealHeight = RobloxMaxImageSize
			RealWidth = (RealHeight / ImageHeight) * ImageWidth
		end
	else
		RealWidth, RealHeight = ImageWidth, ImageHeight
	end
	
	local SingleFrameSize = Vector2.new(RealWidth/frames.Y, RealWidth/frames.X)
    local TotalFrames = frameNumber

	image.ImageRectSize = SingleFrameSize
	
	local CurrentRow, CurrentColumn = 0, 0
	local Offsets = { }
	
	for _ = 1, TotalFrames do
		local CurrentXPosition = CurrentColumn * SingleFrameSize.X
		local CurrentYPosition = CurrentRow * SingleFrameSize.Y
		
		table.insert(Offsets, Vector2.new(CurrentXPosition, CurrentYPosition))
		
		CurrentColumn += 1
		
		if CurrentColumn >= frames.Y then
			CurrentColumn = 0
			CurrentRow += 1
		end
	end
	
	local SpritePlayTime = fps and 1/fps or 0.1
	local CurrentIndex = 0
	
	while image:IsDescendantOf(game) and image:GetAttribute("IsAnimationActive") do
        task.wait(SpritePlayTime)

		CurrentIndex += 1
		image.ImageRectOffset = Offsets[CurrentIndex]
		
		if CurrentIndex >= TotalFrames then
			CurrentIndex = 0
		end
	end
end

--[=[
	Stops the currently playing animation, if any.

	@param image ImageLabel -- The image label that should stop being animated
]=]
function Sprite.StopAnimation(image: ImageLabel)
    if image:GetAttribute("IsAnimationActive") then
        image:SetAttribute("IsAnimationActive", false)
    end
end

return Sprite

Yeah it should give the same result. I’m not sure what you’ve modified in detail, but maybe you might have mixed up some of the X and Y values?

I didn’t simplify my function like how you did it, to prevent confusion between columns, rows, height, width with X and Y values.

In my opinion there’s not much to simplify, or at least not much you can do and still make it easily readable.

1 Like