How to make a FPS crosshair?

What I’m trying to do is to make a FPS crosshair, something similar to this

However I’m not sure what approach to use, my first idea was to use an ImageFrame with the lines, however I have heard of manually making the crosshair lines.

Now if I were to use the ImageFrame method, how would I calculate a Vector3 Unit direction within the frame accounting for the Client’s resolution / viewport size etc. Additionally how would doing something like making the Frame size increase when the gun is fired, and decrease it’s size when not firing?

Some ideas, maybe connect it to MouseButton1Click and increase the size with a Tween. Maybe also make it a ModuleScript to have a function that needs a gunShot value. All just ideas.

You can change the cursor’s image to the crosshair’s image and lock the player into first person mode located in the properties of StarterPlayer

You could for one use UI Size Contraints to for sure lock it in the center of the screen, and then use multiple separate frames for each bit of the crosshair.

After this, you use events (i.e whenever they’re crouching, aiming in, etc) to tween the crosshairs to their corrosponding positions.

Yes but my question is how should calculate a random space in the crosshair and convert it to a Vector3 Unit direction?

Bumping this, any input would be greatly appreciated!

You need to position 4 frames (the hairs or whatever the name of those things are) in an origin position (in this case, the screen center, which you can get by doing AbsoluteSize on a ScreenGui and dividing it by 2) and move them a few pixels away from ut (offset from the origin)
To do this, you just have to do: Frame.Position = OriginPoint + Offset for each of them; both OriginPoint and Offset must be UDim2

For the right and left frames, the offset would only contain only X values and for the top and bottom it would contain Y values

This is some code I could come up with in my spare time

Remember to disable ScreenGui.IgnoreGuiInset

-- Variables

local screenGui = script.Parent
local resolution = screenGui.AbsoluteSize
local origin = UDim2.fromOffset(resolution.X / 2, resolution.Y / 2)
local radius = 15 -- This is our offset number in pixels, we use this to 'move' the frames from our origin
local hairs = {}

-- Settings

-- Size of the frames
local FRAME_X = 5
local FRAME_Y = 1

-- Logic

local function newFrame(): Frame
	local frame ="Frame") -- If you wanted to make something like a shotgun crosshair, you'd probably need a ImageLabel instead
	frame.Name = "_hair"
	frame.Parent = screenGui
	frame.Size = UDim2.fromOffset(FRAME_X, FRAME_Y)
	frame.AnchorPoint =, 0.5)
	-- Add extra settings here if you want
	return frame

local function updateCrosshair()
	local verticalOffset, horizontalOffset = UDim2.fromOffset(0, radius), UDim2.fromOffset(radius, 0) = origin + -verticalOffset
	hairs.bottom.Position = origin + verticalOffset
	hairs.right.Position = origin + -horizontalOffset
	hairs.left.Position = origin + horizontalOffset

local function main()
	local top, bottom, right, left = newFrame(), newFrame(), newFrame(), newFrame()
	-- This is the bottom and top frames so we have to make them thinner in width and larger in height
	top.Size = UDim2.fromOffset(FRAME_Y, FRAME_X)
	bottom.Size = UDim2.fromOffset(FRAME_Y, FRAME_X)
	hairs = {
		top = top,
		bottom = bottom,
		right = right,
		left = left
	-- Constantly update crosshair
	-- You could probably bind it to an event that fires whenever the radius changes instead
	while true do
		--radius = math.abs(math.cos(os.clock())) * 25 -- Testing purposes