this is a script:

-- Variables --

local TeamsService = Game:GetService('Teams')
local PlayersService = Game:GetService('Players')

local PlayerManager = Workspace:WaitForChild('PlayerManager')
local PlayerJoined = PlayerManager:WaitForChild('PlayerJoined')

local BalanceTeams = script:WaitForChild('BalanceTeams')
local AssignTeam = script:WaitForChild('AssignTeam')

local TeamCounts = {}

-- Local Functions --

local function ChangeTeamCount(teamColor, increment)
	TeamCounts[teamColor.Name] = TeamCounts[teamColor.Name] + increment
end

local function RefreshTeamCounts()
	-- Init counts
	for _, team in pairs(TeamsService:GetTeams()) do
		TeamCounts[team.TeamColor.Name] = 0
	end

	-- Get counts
	for _, player in pairs(PlayersService:GetPlayers()) do
		if not player.Neutral then --TODO: What if player not on valid team?
			ChangeTeamCount(player.TeamColor, 1)
		end
	end
end

local function SmallestTeam()
	local smallestTeam = nil
	local smallest = math.huge
	for _, team in pairs(TeamsService:GetTeams()) do
		local teamCount = TeamCounts[team.TeamColor.Name]
		if teamCount < smallest then
			smallestTeam = team
			smallest = teamCount
		end
	end
	return smallestTeam
end

local function LargestTeam()
	local largestTeam = nil
	local largest = 0
	for _, team in pairs(TeamsService:GetTeams()) do
		local teamCount = TeamCounts[team.TeamColor.Name]
		if teamCount > largest then
			largestTeam = team
			largest = teamCount
		end
	end
	return largestTeam
end

-- Unbalanced: Returns true if there is a player disparity of 2 or more
local function Unbalanced()
	local smallestTeamSize = TeamCounts[SmallestTeam().TeamColor.Name]
	for _, team in pairs(TeamsService:GetTeams()) do
		if TeamCounts[team.TeamColor.Name] - smallestTeamSize >= 2 then
			return true
		end
	end
	return false
end

--[[ When a player joins a game with teams, assign them to the smallest one
local function OnPlayerJoined(player)
	RefreshTeamCounts()
	local smallestTeam = SmallestTeam()
	if smallestTeam then
		player.TeamColor = smallestTeam.TeamColor
		player.Neutral = false
		ChangeTeamCount(player.TeamColor, 1)
	end
end
]]

-- Bindable Functions --
--[[
BalanceTeams.OnInvoke = function()
	RefreshTeamCounts()
	local players = PlayersService:GetPlayers()

	-- Assign neutral players
	for _, player in pairs(players) do
		if player.Neutral then
			local smallestTeam = SmallestTeam()
			player.TeamColor = smallestTeam.TeamColor
			player.Neutral = false
			ChangeTeamCount(player.TeamColor, 1)
		end
	end

	while Unbalanced() do
		-- Move one player from the largest team to the smallest team
		local largestTeam = LargestTeam()
		local smallestTeam = SmallestTeam()
		for _, player in pairs(players) do
			if player.TeamColor == largestTeam.TeamColor then
				ChangeTeamCount(player.TeamColor, -1)
				player.TeamColor = smallestTeam.TeamColor
				ChangeTeamCount(player.TeamColor, 1)
				break
			end
		end
	end
end
--]]

--PlayerJoined.Event:connect(OnPlayerJoined)

AssignTeam.OnInvoke = function(player)
	if player then
		RefreshTeamCounts()
		local smallestTeam = SmallestTeam()
		if smallestTeam then
			player.TeamColor = smallestTeam.TeamColor
			player.Neutral = false
			ChangeTeamCount(player.TeamColor, 1)
		end
	end
end