How can I recreate this effect?

I’ve been trying to create a News Ticker type effect for about 2 hours now, but I’m not sure how.
The image below shows what I’ve been trying to create:


Though here are the results of this:

I’m not sure what the issue with the script entirely is, but pointing the obvious out, the images are not following each other in order nor moving in unison.

I’ve tried to search this up multiple times on the DevForum and Reddit which I could barely find any posts related to this since News Tickers aren’t as in demand.

ModuleScript:

local textModule = {}

local PlayersService = game:GetService("Players")
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")
local DebrisService = game:GetService("Debris")

local waitTime = 8
local runTime = 1

local function convertText(text)
	return string.upper(text)
end

local function typewrite(label1, label2, text, delay)
	for i = 1, #text do
		label1.MaxVisibleGraphemes = i
		label2.MaxVisibleGraphemes = i
		task.wait(delay)
	end
end

function textModule:new(player, colorScheme, text)
	if not (player and colorScheme and text) or type(text) ~= "string" then return end

	local plrGui = player:FindFirstChild("PlayerGui")
	if not plrGui then return end

	local templateUI = script:WaitForChild("PhaseUI")
	local templateImage = script:WaitForChild("TemplateImage")
	templateImage.Inner.ImageColor3 = colorScheme

	local UI = templateUI:Clone()
	UI.Parent = plrGui

	local CanvasGroup = UI:WaitForChild("GroupedFrame")
	local Frame = CanvasGroup:WaitForChild("MainFrame")
	local ConveyorFrameLeft = CanvasGroup:WaitForChild("UpperToLeft")
	local ConveyorFrameRight = CanvasGroup:WaitForChild("LowerToRight")

	local Text = Frame.MainText
	local Text2 = Text.ActualText

	CanvasGroup.GroupColor3 = Color3.new(0, 0, 0)
	CanvasGroup.GroupTransparency = 1
	Frame.BackgroundColor3 = colorScheme

	TweenService:Create(CanvasGroup, TweenInfo.new(0.35, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut), {
		GroupColor3 = Color3.new(1,1,1),
		GroupTransparency = 0
	}):Play()

	local upperText = convertText(text)
	typewrite(Text, Text2, upperText, 0.05)

	local function startConveyor(conveyorFrame)
		local templateSize = templateImage.Size.X.Scale
		local spacing = 0.05
		local clones = {}

		local function clone()
			local clone = templateImage:Clone()
			clone.Parent = conveyorFrame
			clone.Position = UDim2.fromScale(1,0)
			clone.ImageTransparency = 0
			
			table.insert(clones, clone)
		end

		for i = 1, 3 do
			clone()
		end

		local function resetPosition(clone)
			clone.Position = UDim2.fromScale(1,0)
		end

		local function updateLabels(dt)
			for i, clone in ipairs(clones) do
				clone.Position = clone.Position - UDim2.fromScale(dt / runTime, 0)
				
				if clone.Position.X.Scale <= -templateSize then
					resetPosition(clone)
					clone()
				end
				-- wait a bit
				task.wait(0.05)
			end
		end
		RunService.RenderStepped:Connect(updateLabels)
	end

	task.spawn(function() 
		startConveyor(ConveyorFrameLeft) 
	end)
	task.spawn(function() 
		startConveyor(ConveyorFrameRight) 
	end)

	task.wait(7.25)
	TweenService:Create(CanvasGroup, TweenInfo.new(0.35, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut), {
		GroupColor3 = Color3.new(0,0,0),
		GroupTransparency = 1,
		Size = UDim2.fromScale(1.2,1.2)
	}):Play()

	DebrisService:AddItem(UI, waitTime)
end

return textModule

To be honest, make a frame with all the warning symbols, then move it to the left or right until it looks like it’s back at its original position, but it’s actually offset to the left or right by one symbol, then snap the position back to the original and repeat. This is how most games do it. For the typewriter refract the easier way is to just concat a string every 0.1 seconds with the next character and display that. All of his should be under 20 lines.

1 Like

Yeah I’ll try this and see if it works. I’ve seen a lot of models over the years create this effect like that, but it usually ends up breaking for me.

It may be a timing/spacing issue. Best guess.

local function startConveyor(conveyorFrame)
    local templateSize = templateImage.Size.X.Scale
    local spacing = 0.05
    local clones = {}
    local totalSpacing = templateSize + spacing

    -- Initialize clones with proper spacing
    for i = 1, 3 do
        local clone = templateImage:Clone()
        clone.Parent = conveyorFrame
        clone.Position = UDim2.fromScale(1 + totalSpacing * (i - 1), 0)
        clone.ImageTransparency = 0
        table.insert(clones, clone)
    end

    local function resetPosition(clone, index)
        clone.Position = UDim2.fromScale(1 + totalSpacing * (index - 1), 0)
    end

    local function updateLabels(dt)
        for i, clone in ipairs(clones) do
            clone.Position = clone.Position - UDim2.fromScale(dt / runTime, 0)

            if clone.Position.X.Scale <= -templateSize then
                resetPosition(clone, #clones)
                table.insert(clones, table.remove(clones, 1))
            end
        end
    end

    RunService.RenderStepped:Connect(updateLabels)
end

Didn’t really help, it only cloned 3 images on each side and that was all.

You’re actually making a type of side-scroller. Unless you’re on an Amiga, other PCs struggle with this.
IBM PC clones have historically been much better at handling vertical movement compared to horizontal (side-scrolling).