Recreate a Roblox Profile Thumbnail in ViewPort

I’m trying to recreate the official Roblox thumbnail for profile pictures they provide via Players:GetUserThumbnailAsync
But I believe they are making some modifications to a lot of different attributes of the Camera depending on the clothes/parameters of the character.

I’ll show a simple code of straight ahead “front” shot of the icon

	local character = player.Character

	local humanoid = character:WaitForChild("Humanoid")

	local humanoidDescription = humanoid:GetAppliedDescription()

	local model = Players:CreateHumanoidModelFromDescriptionAsync(
		humanoidDescription,
		Enum.HumanoidRigType.R15,
		Enum.AssetTypeVerification.Always
	)

	local animate = model:FindFirstChild("Animate")
	animate:Destroy()

	local worldModel = iconPreviewViewport:WaitForChild("WorldModel")
	model.Parent = worldModel

	model:PivotTo(CFrame.new(0, 0, -2) * CFrame.Angles(0, math.pi, 0))

	local head = model:FindFirstChild("Head") :: BasePart?

	local camera = Instance.new("Camera")
	iconPreviewViewport.CurrentCamera = camera
	camera.Parent = iconPreviewViewport

	local focus = head.Position
	local distance = head.Size.Y * 2.5
	local lookVector = model:GetPivot().LookVector

	camera.CFrame = CFrame.new(focus + (lookVector * distance), focus)

But you can clearly see from original Roblox thumbnails that they change at least the FOV parameter, and I want to get it working same as they do in ViewportFrame.

3 Likes

as far as im aware there isnt a way to get the specific measurements of the avatar thumbnail

not sure if this is relevant to you but you can use rbxthumb to show the user’s avatar that way
rbxthumb://type=AvatarHeadShot&id=13527670&w=720&h=720

What we did to get it perfectly on the mesh tool thumbnails we did back then was trial and error, we downloaded a thumbnail image, slapped it over the viewport and edited the properties until it worked.

Roblox doesn’t tell us anything (how surprising)

I believe that picture, that is just a picture, is 420 × 420.

camera.FieldOfView = 70
camera.CFrame = CFrame.new(focus + Vector3.new(0, head.Size.Y * 2.5, distance), focus)

You may want to slightly adjust the camera Y offset for “eye-level” framing like Roblox does.
Use 420 × 420 for your ViewportFrame size to match the standard thumbnail.

This could be totally wrong, but this is what I could gather from Thumbnails/ThumbnailUtils.js in js.rbxcdn.com (the source for 3D thumbnails)

Camera Setup and FOV

  • The camera uses a PerspectiveCamera with a FOV that is often 70 degrees, though sometimes it’s read from the JSON (json.camera.fov).
  • Aspect ratio is container width / height.
  • Near/far planes are something like 0.1 (min) and calculated maxRenderDistance (max), which in Roblox terms could be model:GetExtentsSize().Magnitude * factor.

Camera Positioning

  • The camera is positioned relative to the bounding box of the model (json.aabb) so the whole character fits in the frame.
  • Look-at point is the center of the model.
  • Distance from the model is typically 4× the model bounding box length, but you could adjust this based on head size for a front-facing portrait.
  • Roblox sometimes flips the model so it faces the camera: CFrame.Angles(0, math.pi, 0) in Lua is equivalent.

Lighting

  • There are two modes: dynamic and static.
  • Static lighting is simpler: an ambient light plus two directional lights (sun and backlight).
  • Dynamic lighting adds three lights: ambient, keylight, fill light, rim light.
  • Colors are soft grays: 0xd4d4d4, 0xacacac, 0x444444.

So it might look something like this:

local model = Players:CreateHumanoidModelFromDescriptionAsync(
		humanoidDescription,
		Enum.HumanoidRigType.R15,
		Enum.AssetTypeVerification.Always
	)-- your model
local head = model:FindFirstChild("Head")
local camera = Instance.new("Camera")
iconPreviewViewport.CurrentCamera = camera
camera.Parent = iconPreviewViewport

local size = model:GetExtentsSize()
local center = model:GetPivot().Position

local distance = size.Magnitude * 2 -- adjust factor
local lookVector = (center - (center + Vector3.new(0,0,distance))).Unit

camera.CFrame = CFrame.new(center + Vector3.new(0,0,distance), center)
camera.FieldOfView = 70

I can try to dig more into the file, but so far this seems to be correct just by running the javascript through an analysis. The best thing you can do is just read over the .js files that are used to create the character image, and reversing them. You can do this easily by going to your profile and running Ctrl + Shift + I and inspecting Sources

3 Likes