Zyntex Experiments - Create free A/B tests in real-time

Zyntex Experiments banner

Zyntex now includes unlimited Experiments for every plan, including Free. Create A/B tests, route players into control and treatment groups, track entries and conversions, and see results populate live on your cloud dashboard.

Studios already using Zyntex rely on it for live operations, logs, moderation, telemetry, and now evidence-based iteration on UI, economy, and retention.


:sparkles: What you can test

  • UI and UX: Shop layouts, button placements, HUD elements, tutorial steps.
  • Monetization: Prices, bundles, gamepass mixes, timed offers.
  • Onboarding and retention: First session flows, tips, FTUE pacing.
  • Balance and pacing: Weapon stats, XP curves, difficulty ramps.
  • Rollouts: Ship a feature to a percentage of players, validate impact, then scale.
How Experiments work
  1. Player joins a server.
  2. Zyntex assigns the player to a group by random assignment that is stable across servers.
  3. Your game registers an entry when the player hits your experiment surface, for example opening the shop.
  4. Your game registers a conversion when the player completes the goal, optionally with a numeric value, for example robux spent.
  5. The dashboard aggregates conversion rate, lift, p value, and more, so you can decide which variant wins.

:rocket: Quickstart

1) Create an experiment

Open your game in the Zyntex dashboard, go to Experiments, click + New Experiment, define groups and distribution.

2) Add the SDK and initialize

local Zyntex = require(path.to.zyntex)("YOUR-GAME-TOKEN")

Zyntex:init {
    debug = false,
}

3) Fetch the experiment and get the player group

local Experiment = Zyntex:GetExperiment("shop_experiment")

if Experiment:GetStatus() ~= "active" then
    -- paused or archived
    return
end

local group = Experiment:GetGroup(player) -- registers an entry once per player
if group.id == "shop_new" then
    -- show new UI
else
    -- show old UI
end

4) Record a conversion with optional value

-- for example, after a purchase
group:Convert(100) -- value is optional, defaults to 0
Minimal server plus client example
-- Server
local Players = game:GetService("Players")
local RS = game:GetService("ReplicatedStorage")
local ExperimentEvent = RS:WaitForChild("Experiment")

local Zyntex = require(path.to.zyntex)("YOUR-GAME-TOKEN")
Zyntex:init { debug = false }

local Experiment = Zyntex:GetExperiment("shop_experiment")
local totals = {} -- [Player] = { value = 0, group = Group }

Players.PlayerAdded:Connect(function(plr)
    if Experiment:GetStatus() ~= "active" then return end

    local grp = Experiment:GetGroup(plr) -- registers entry, sticky per player
    totals[plr] = { value = 0, group = grp }

    if grp.id == "shop_new" then
        ExperimentEvent:FireClient(plr, "new")
    else
        ExperimentEvent:FireClient(plr, "old")
    end
end)

ExperimentEvent.OnServerEvent:Connect(function(plr, amount)
   if totals[plr] then
       totals[plr].value += tonumber(amount) or 0
   end
end)

Players.PlayerRemoving:Connect(function(plr)
    local t = totals[plr]
    if t then
        t.group:Convert(t.value or 0)
        totals[plr] = nil
    end
end)
-- Client
local RS = game:GetService("ReplicatedStorage")
local ExperimentEvent = RS:WaitForChild("Experiment")
local ShopUIs = RS:WaitForChild("ShopUIs")

ExperimentEvent.OnClientEvent:Connect(function(which)
    if which == "new" then
        ShopUIs.NewShopUI:Clone().Parent = game.Players.LocalPlayer.PlayerGui
    else
        ShopUIs.OldShopUI:Clone().Parent = game.Players.LocalPlayer.PlayerGui
    end
end)

-- call this when a purchase happens, client or server side
local function OnPurchase(amount)
    ExperimentEvent:FireServer(amount)
end

:bar_chart: Analyzing results

Your dashboard shows:

  • P value: Probability that the observed difference is due to chance. Below 0.05 is commonly treated as significant.
  • Z score: Standard score indicating direction and strength of the effect.
  • Absolute lift: Difference in conversions between groups.
  • Relative lift: Percentage change in conversion rate between groups.
  • Standard error: Precision of the estimate, lower is better.

When the control loses and the treatment wins with acceptable significance, ship the winner and archive the experiment.


:white_check_mark: Best practices

  • Start with an A/A test to validate your wiring and traffic split.
  • Run for at least one week to smooth day of week effects.
  • Avoid overlapping tests on the same surface to reduce interference.
  • Choose one clear goal per experiment, for example purchase, level completion, retention event.
  • Watch plan limits if your game can exceed your monthly tracked player cap, unregistered players will not be enrolled in experiments.
FAQ

Do players ever switch groups after assignment
No, assignment is stable for a player across servers, so they see the same variant.

Can I run multi group tests
Yes, define more than two groups and set custom distributions.

Does this work on mobile heavy games
Yes, the dashboard is mobile compatible and assignment works the same.

What if my game spikes above my plan
Players over your plan cap will not be registered by Zyntex, which means they will not enter the experiment.


:test_tube: Try Experiments free today

Operate with data, not guesses. Create your first A/B test in one afternoon.

Have questions or feedback, drop them below or reach out on Discord!

2 Likes

Seems fishy but I may just be spectacle, the whole link your game using a require is kinda iffy for me,

This seems like a good resource in hindsight.

This is something that I would prefer to see a video examples of and or more documentation in order to trust it.

Looking at the module it just does a POST request to a domain.

--[[
	Zyntex Linking module
	Written by: Deluct
	
	This module allows you to link your game to the Zyntex dashboard.
]]
local HTTP = game:GetService("HttpService")
return function(gameToken: string)
	local response = HTTP:RequestAsync({
		["Url"] = "https://api.zyntex.dev/links/games/link";
		["Method"]  = "POST";
		["Headers"] = {
			["Authorization"] = `Game-Token {gameToken}`,
			["Content-Type"] = "application/json"
		} 
	})
	
	response = HTTP:JSONDecode(response.Body)
	if response.success == false then
		error(`[Zyntex]: Linking failed: {response.user_message}`)
	end
	print(`[Zyntex]: Linking success!`)
end

No worries! You can see our documentation here and as the other developer pointed out its a simple POST request to our domain.

1 Like