Thanks a lot for this. Still struggling to set this up smh; not sure where to run the scripts the OP mentions above; command prompt or?
Sorry for taking so long to reply, but the local scripts should ideally be placed in starter player scripts because the effect only works on the client and needs to run at runtime. The module you can have anywhere you want, also ideally in replicated storage.
an Update after like months. I fixed a lot of the math and made the code more performant. The rotated surfaces should work, same with the rotated images. So hopefully everything will work a lot better.
Hello, this resource is awesome! Roblox must have been using this technology for their event portals. I found a few issues though, which I had to fix.
- ParallaxWindow folder is destroyed on CharacterAdded
- When parts stream out, the SurfaceGui associated with the part is destroyed
- No proper support for UIStroke or Face UICorner
- No type annotation
- Poorly organized code in my opinion
Patched ParallaxWindow (30.8 KB)
In addition to this, I’ll release Fusion Components for ParallaxWindow!
ParallaxWindow Fusion Components.rbxl (134.9 KB)
Source Code
--[[
avocado (@avodey) -w-
Creates the visual you see in the video!
1/7/2025
]]
local RunService = game:GetService("RunService")
local Replicated = game:GetService("ReplicatedStorage")
local TweenService = game:GetService("TweenService")
local Fusion = require(Replicated.Fusion)
local scoped, Children = Fusion.scoped, Fusion.Children
local scope = scoped(Fusion, {
ParallaxWindow = require(Replicated.ParallaxWindow),
ParallaxFrame = require(Replicated.ParallaxFrame),
})
local start = tick()
local void = script.Parent:WaitForChild("Void")
scope:ParallaxWindow {
CornerRadius = UDim.new(1, 0),
LightInfluence = 0,
Brightness = 5,
[Children] = scope:Computed(function(_, scope)
local children = {}
local count = 18
local spread = 1
for i = 1, count do
local layer: Fusion.Scope = scope:innerScope()
local wave = layer:Value(i)
local spring = layer:Spring(layer:Value(0), 20, 0.2)
local size = 1 - (i - 1) / (count + 1) * 0.5
table.insert(layer, RunService.RenderStepped:Connect(function()
local time = tick() - start + i / count
wave:set(math.cos(time * 1.5))
end))
table.insert(layer, void.ClickDetector.MouseClick:Connect(function()
task.wait((i - 1) / count * 0.25)
spring:addVelocity(50)
end))
table.insert(children, layer:ParallaxFrame {
Adornee = void,
Face = Enum.NormalId.Left,
Brightness = 3,
Position = layer:Computed(function(use)
return -Vector3.new(i * spread + use(spring))
end),
[Children] = {
layer:New "Frame" {
AnchorPoint = Vector2.one / 2,
Position = UDim2.fromScale(0.5, 0.5),
Size = UDim2.fromScale(size, size),
BackgroundTransparency = 1,
[Children] = {
layer:New "UIStroke" {
Color = Color3.new(0, 1, 0),
Transparency = (i - 1) / count,
Thickness = 2,
},
layer:New "UICorner" {
CornerRadius = UDim.new(1, 0)
}
}
},
},
})
end
return children
end)
}
If you find any issues with these components, with the ParallaxWindow modifications, or with the original module, please let me know! I’m using this resource, so it would be beneficial to know of any fixes that should be implemented.
This is great. This was really the first module I’ve ever done so I used someone else’s module as a frame for it, which hopefully explains why its so poorly organised. I’m glad someone came along and fixed many of the problems that I didn’t fix. I’m not so sure about the module actually being performant as it uses multiple CanvasGroups, which for some reason tank the rendering performance, but hopefully the module is still useful!
Fusion is not required to run ParallaxWindow, but you need it to use my components that drive it.
Hey, cheesyr from hidden devs here, I was scrolling around resources and found this, I am actually EXTREMELY impressed!!! Not gonna lie, I’m gonna try making a Portal game with this!
In a project I am making, I’m using parallax to create a 3D effect on cards. When inspecting them, the card is facing exactly in front of the camera, and this causes a bug where the parallax elements sometimes begin to flicker.
I found that this is because in the function calcParallax, when the camera facing is exactly in front of the surface, horOffset and verOffset sometimes gets calculated to nan.
I added these lines of code in calcParallax to fix. (I don’t actually know the underlying problem with why its calculating nan tbh)
-- check if the offsets are nan
if horOffset ~= horOffset then horOffset = 0 end
if verOffset ~= verOffset then verOffset = 0 end
Is that Hatsune Miku? If so, cool.
And also if so, how legal is that? Lol.
Hello! I don’t use the @bluebxrrybot account anymore.
I can reproduce this issue, thanks for bringing it up! :3
I found that the issue is caused when the container’s 3D position is exactly the same to the surface 3D position. The distance between them is zero, which is used for calculating some dot products. The result is NaN because the Wall vectors were multiplied by zero, causing the issue.
Your fix works, but I decided to do something simplier to fix the issue:
if imageDist == 0 then
horizOffset, vertOffset = 0, 0
end
This is what the patched version looks like:
MODULE ParallaxWindow.lua (29.6 KB)
PLACE ParallaxNanFix.rbxl (119.6 KB)
NOTE: FrameSettings.Position now uses the Z axis to change depth instead of the X axis. I was annoyed because of the confusing behavior, so I changed it.
Do u have any docs for it? I am trying to use it but I am struggling a little, docs or somewhere to look for, would be great, ty!
My version of the module is pretty similar, the API shouldn’t be much different. There’s a few notable changes that I’ve listed.
- FrameSettings.Position.Z is now the depth. When negative, it looks farther away. When positive, it looks closer to you.
- Type interface
- Bug fixes, optimization
- More organized code
I’m currently making and testing a massive overhaul of this module, with even more organization, optimization, multithreading, and ease of use. Stuck on frustum culling right now!
I tried your new version where u fix some issues, but that one is kinda different how the logic works, anyways, I will wait patiently!
I understand. Was there anything specific you needed help with? I forgot how that one is laid out.
I mean, nothing specific rn, just testing the module and seeing how it works
are u able to give me ur discord so I can ask some questions?