Automatic Material Variant Sorter


Did you ever get an awesome kit of materials and realize theres so many you physically cannot see every possible texture without putting them all on parts? Got a PBR kit or a thousand materials in material service that you are no way in hell sorting though? Wish there was an easier solution? WELL BOY DO I GOT A PRESENT FOR YOU!

Introducing a (pretty simple) automatic MaterialService Sorter that puts EVERY material variant from MaterialService into very clean (and customizable) rows that allow you at a glance to see what you’re working on. ADDED BONUS? IT MAKES SIGNS AUTOMATICALLY TOO!

Every Material Variant will be organized by category (example “Concrete”) taken from the basematerial and placed into neat rows.


Usage of this system is very simple, all you need is to open command bar (View > Command Bar) and type in the following while in studio after inserting the model.

local Req = require(game.Workspace["Auto Material Sorter"].ModuleScript) Req:Sort()

And with that its that simple! It’ll automatically filter through all of the materials in MaterialService and place them into neat lines.

(Just a heads up, this uses GetDescendants and will lag for a few seconds if you have over a thousand materials. Just saying, give it a few seconds.)

Oh right its also free, who doesn’t like free stuff?

Marketplace Link

Source Code

Total Time: About an hour

local module = {}

-- TO USE TYPE "local Req = require(game.Workspace["Auto Material Sorter"].ModuleScript) Req:Sort()" INTO COMMAND BAR.

local MaxTiling = 50  -- Determines how many materials per row
local YOffsetDistance = -4 -- Determines Studs in Between Rows
local GuideOffsetDistance = 15 -- Determines distance between each sign (May need to make this higher if materials are overlapping)

function module:Sort()
	-- Summons MaterialVariants through MaterialService
	local Template = script.Parent.Guide:Clone() Template:ClearAllChildren() Template.Size =, 1, 2)

	for _, p in pairs(game.MaterialService:GetDescendants()) do
		if p:IsA("MaterialVariant") then
			local I = Template:Clone() I.Parent = script.Parent.Materials I.Material = p.BaseMaterial I.MaterialVariant = p.Name
	-- Begins Sorting

	for _, Mat in pairs(Enum.Material:GetEnumItems()) do  -- This was fun to find, didn't know you could get an enum table
		script.Parent.Guide.CFrame = script.Parent.Guide.CFrame *, 0, 0) -- Moves the guide bar

		local L = script.Parent.Guide:Clone() L.Parent = script.Parent.Signs L.SurfaceGui.TextLabel.Text = Mat.Name
		local C = 0 local Y = 0

		for _, p in pairs(script.Parent.Materials:GetChildren()) do
			if p:IsA("BasePart") and p.Material == Mat then
				C += 1 if C > MaxTiling then C = 1 Y += 1 end
				p.CFrame = script.Parent.Guide.CFrame *, 0, YOffsetDistance * Y) *, 0, -GuideOffsetDistance - 4 * C)

		if C == 0 and Y == 0 then -- No materials of this variant no problem, just deletes them
			script.Parent.Guide.CFrame = script.Parent.Guide.CFrame *, 0, 0)

	for _, p in pairs(script.Parent.Signs:GetChildren()) do
		p.Parent = script.Parent.Materials

	script.Parent.Materials.Parent = game.Workspace script.Parent:Destroy()

return module