New SDK Preview
This section is a preview of the new SDK. The new SDK is still in development, and we are actively working on improving it. We value your feedback, so if you have any questions or suggestions, please reach out to us. If you are not using the new SDK, please refer to another documentation section.
Balancy UI Builder Documentation¶
Table of Contents¶
- Introduction
- UI Builder
- JavaScript API Reference
- Data Attributes System
- Events System
- Global Variables
- Common Patterns
- Battle Pass Integration
Introduction¶
The Balancy UI Builder allows developers to create custom user interfaces using HTML, CSS, and JavaScript. The system automatically injects a powerful JavaScript API that provides seamless integration with the Balancy backend, automatic element management, and cross-platform communication.
UI Builder¶
[This section will be populated with UI builder interface documentation, screenshots, and tutorials]
UI Builder is available for editing specific type of Assets called Views. To open UI Builder, go to Assets page and select View tab. The page will show Views you currently have in your project and Create button to create a new one. In the context menu of a View there is Open action, as it is shown here:
In general, UI Builder will look like this:
It has main working area with the View's layout, tool panel on the right with basic elements, navigation panel with all the content of the View, and the tool panel on the top with additional controls.
What is View?¶
View is a specific type of asset combining HTML, CSS, and JavaScript code. Views are user interfaces that can be used to visualise game events and offers of any kind. Views are being opened in Webview provided and controlled by Balancy SDK.
Tools Panel¶
Located in the right side, it provides a collection of basic elements to use as building blocks for Views, allows to set up element's style, and to manage additional settings for elements, such as dynamic data or information to connect them to JavaScript.
All three Tabs are shown here:
Basic blocks are:
- Containers - many combinations, needed to build adaptive layouts
- Images - static art assets, connected to Balancy resurces. Regular or 9-sliced
- Texts - static, localized, and dynamic texts
- Buttons - always 9-sliced images with an option to assign actions
- Links - URLs to external resources
- Video - for embedding videos
For selected element tool panel will show styling params to control position, layout constraints, size, typography, and additional effects.
For such elements like images and texts there are additional parameters on Settings tab, such as image source, text source, button action, string formatting, 9-slice image mapping, etc.
Hierarchy view¶
Located on the left side, navigation panel shows a tree view with all elements used in current View, providing the way to rearrange layout and select elements for editing. Elements can be moved between containers via simple drag-n-drop.
Global Script¶
JS code embedded, Battle Pass example
Previewing in Demo App¶
saving and playing using embedded demo app
Using Views in LiveOps¶
game events and offers, placements
JavaScript API Reference¶
The Balancy framework automatically injects the balancy
object into your JavaScript environment, providing access to backend data, user profiles, and platform-specific functionality.
balancy.findDomElement(dataId, parentElement?)
¶
Finds an element with the specified data-id
attribute.
Parameters:
dataId
(string): The data-id value to search forparentElement
(HTMLElement, optional): Parent element to search within (defaults to document)
Returns: HTMLElement | null
Example:
const headerElement = balancy.findDomElement('window-header');
const button = balancy.findDomElement('claim-button', headerElement);
balancy.setImage(element, sprite)
¶
Sets an image on an HTML element using a Balancy sprite object.
Parameters:
element
(HTMLElement): Target element (IMG tag or element with background)sprite
(object): Sprite object with id property
Example:
const iconElement = document.getElementById('reward-icon');
balancy.setImage(iconElement, reward.item.unnyIcon);
balancy.getSystemProfileValue(path)
¶
Retrieves a value from the system profile (UnnyProfile).
Parameters:
path
(string): Dot-separated path to the desired value
Returns: Promise<any>
Example:
const playerLevel = await balancy.getSystemProfileValue('GeneralInfo.Level');
//playerLevel = 5
const generalInfo = await balancy.getSystemProfileValue('GeneralInfo');
//generalInfo example (Object)
// {
// "level": 0,
// "country": "CY",
// "session": 68,
// "customId": "Custom456",
// "deviceId": "30438f52-d81e-4065-8607-298c98626ecc",
// "platform": "Unknown",
// "playTime": 18295,
// "isNewUser": false,
// "profileId": "528c0854-54d8-11f0-b905-1fec53a055ba",
// "appVersion": "1.0.0",
// "currentDay": 20297,
// "deviceName": "",
// "deviceType": 0,
// "platformId": -1,
// "deviceModel": "",
// "dontDisturb": false,
// "offlineTime": 1,
// "tutorialStep": 0,
// "engineVersion": "React_1.0",
// "lastLoginTime": 1753712746,
// "trafficSource": "",
// "firstLoginTime": 1753186128,
// "installVersion": "1.0.0",
// "systemLanguage": "",
// "operatingSystem": "",
// "trafficCampaign": "",
// "gameLocalization": "en",
// "systemMemorySize": 0,
// "timeSinceInstall": 526622,
// "balancyPlatformId": 7,
// "timeSincePurchase": 338642,
// "operatingSystemFamily": 0
// }
balancy.getProfileValue(profile, path)
¶
Retrieves a value from a specific profile.
Parameters:
profile
(string): Profile namepath
(string): Dot-separated path to the desired value
Returns: Promise
Example:
const value = await balancy.getProfileValue('PlayerData', 'Achievements.Completed');
balancy.getDocumentValue(id, depth)
¶
Retrieves a document from Balancy with specified depth.
Parameters:
id
(string): Document IDdepth
(number): Depth of nested objects to fetch
Returns: Promise
Example:
const config = await balancy.getDocumentValue('245', 3);
console.info(config);
balancy.getLocalizedText(key)
¶
Gets localized text for the specified key.
Parameters:
key
(string): Localization key
Returns: Promise
Example:
const welcomeText = await balancy.getLocalizedText('welcome_message');
document.getElementById('title').textContent = welcomeText;
balancy.getImageUrl(id)
¶
Gets the URL for an image asset.
Parameters:
id
(string): Image asset ID
Returns: Promise
Example:
const imageUrl = await balancy.getImageUrl('reward-icon-123');
document.getElementById('icon').src = imageUrl;
balancy.closeView(source)
¶
Closes the current view.
Parameters:
source
(string): Reason for closing (for debugging)
Example:
balancy.closeView('Purchase completed');
balancy.sendIsReady()
¶
While loading a page, Balancy automatically injects sprites and fonts, once everything is ready it shoots balancy-ready
event.
Right after this event Balance View notifies the SDK that it's ready and is being shown to the user. If you want to postpone and load any additional data, you can delay the ready signal:
Example:
function main() {
balancy.delayIsReady();
//some async logic...., then
balancy.sendIsReady();
}
window.addEventListener('balancy-ready', main, { once: true });
balancy.getBattlePassConfig()
¶
Gets the configuration for the current battle pass.
Returns: Promise<object>
- Battle pass configuration
Example:
const config = await balancy.getBattlePassConfig();
console.log('Scores required:', config.scores);
console.log('Reward lines:', config.rewards);
Response Structure:
{
unnyId: "995",
name: "LIVEOPS TEMPLATES/KEY_SEASON_PASS_NAME",
scores: [10, 20, 30, 40, 50, 60], // XP required for each level
rewards: [
{
unnyId: "1007",
name: "Free Track",
rewards: [
{
unnyId: "1002",
count: 1,
item: {
unnyId: "826",
name: "Gems",
unnyIcon: { id: "819", type: 1 }
}
}
// ... more rewards
]
}
// ... more reward lines
]
}
balancy.getBattlePassProgress()
¶
Gets the current player's battle pass progress.
Returns: Promise<object>
- Battle pass progress
Example:
const progress = await balancy.getBattlePassProgress();
console.log('Current level:', progress.level);
console.log('Current XP:', progress.scores);
Response Structure:
{
level: 1, // Current level (0-based)
scores: 15, // Current XP/scores
finished: false, // Whether battle pass is completed
progressInfo: [
{
progress: [1, 0, 0, 0, 0, 0], // Claim status for each level (0=locked, 1=available, 2=claimed)
available: true, // Whether this reward line is available
unnyIdReward: "1007" // Reward line ID
}
// ... more reward lines
]
}
balancy.claimBattlePassReward(lineId, index)
¶
Claims a battle pass reward.
Parameters:
lineId
(string): Reward line IDindex
(number): Level index to claim
Returns: Promise<boolean>
- Success status
Example:
const success = await balancy.claimBattlePassReward('1007', 0);
if (success) {
console.log('Reward claimed successfully!');
}
balancy.canBuyGroupOffer(index)
¶
Checks if a group offer can be purchased.
Parameters:
index
(number): Offer index
Returns: Promise<boolean>
Example:
const canBuy = await balancy.canBuyGroupOffer(0);
document.getElementById('buy-button').disabled = !canBuy;
balancy.formatTime(seconds)
¶
Formats time duration into a human-readable string.
Parameters:
seconds
(number): Duration in seconds
Returns: string
Example:
const timeLeft = balancy.getTimeLeft();
const formatted = balancy.formatTime(timeLeft);
// Returns: "2d 5h" or "01:23:45"
balancy.getTimeLeft()
¶
Gets remaining time based on balancySettings.
Returns: number - Seconds remaining
Example:
const timeLeft = balancy.getTimeLeft();
document.getElementById('timer').textContent = balancy.formatTime(timeLeft);
balancy.formatDataTemplate(formatStr, data)
¶
Formats a template string with data values.
Parameters:
formatStr
(string): Template string with {path} placeholdersdata
(object): Data object
Returns: string
Example:
const template = "You have {currency.coins} coins and {level} level";
const data = { currency: { coins: 100 }, level: 5 };
const result = balancy.formatDataTemplate(template, data);
// Returns: "You have 100 coins and 5 level"
Constants and Enums¶
RequestAction¶
const RequestAction = {
None: 0,
GetProfile: 1,
SetProfile: 2,
GetDocument: 3,
GetLocalization: 10,
GetImageUrl: 11,
GetInfo: 12,
CanBuyGroupOffer: 13,
GetBattlePassProgress: 20,
BuyOffer: 101,
BuyGroupOffer: 102,
BuyShopSlot: 103,
BattlePassClaim: 104,
CloseWindow: 200,
BalancyIsReady: 201,
CustomMessage: 1000
};
InfoType¶
const InfoType = {
None: 0,
OfferPrice: 1,
OfferGroupPrice: 2,
TimeLeft: 3,
CustomPrice: 9,
Custom: 10
};
BattlePassStatus¶
const BattlePassStatus = {
NotAvailable: 0,
Available: 1,
Claimed: 2
};
Data Attributes System¶
Balancy automatically processes HTML elements with special data attributes, providing declarative functionality without JavaScript code.
Button Actions¶
Add data-button-action
to make any element interactive:
<!-- Buy offer button -->
<button data-button-action="101" data-button-params='{"productId": "coin_pack_1"}'>
Buy Coins
</button>
<!-- Buy group offer -->
<button data-button-action="102" data-index="0">
Buy Special Pack
</button>
<!-- Claim battle pass reward -->
<button data-button-action="104" data-index="2">
Claim Reward
</button>
Available Attributes:
data-button-action
: Action ID from RequestAction enumdata-button-params
: JSON string with additional parametersdata-index
: Index parameter for certain actions
Button Events:
balancyButtonResponse
: Fired when action completesbalancyButtonError
: Fired on error
Example Event Handling:
button.addEventListener('balancyButtonResponse', (event) => {
const { success, result, actionId } = event.detail;
if (success) {
console.log('Action completed:', result);
}
});
Dynamic Text Updates¶
Elements with data-text-type="dynamic"
automatically update with backend data:
<!-- Show offer price -->
<span data-text-type="dynamic"
data-info-type="1"
data-text-format="{price} USD">
</span>
<!-- Show time remaining -->
<span data-text-type="dynamic"
data-info-type="3"
data-text-format="Time left: {time}">
</span>
<!-- Custom data -->
<span data-text-type="dynamic"
data-info-type="10"
data-custom="player_stats"
data-text-format="Level {level} - {xp} XP">
</span>
Available Attributes:
data-text-type="dynamic"
: Marks element for dynamic updatesdata-info-type
: Type of information to fetch (InfoType enum)data-text-format
: Template string for formattingdata-custom
: Custom parameter for InfoType.Customdata-product-id
: Product ID for price queriesdata-index
: Index for group offers
Automatic Localization¶
Elements with data-text-type="localized"
automatically get localized:
<h1 data-text-type="localized" data-localization-key="welcome_title">
<!-- Will be replaced with localized text -->
</h1>
<button data-text-type="localized" data-localization-key="buy_button">
Buy Now
</button>
Automatic Image Loading¶
Elements with data-image-id
automatically load images:
<!-- For IMG elements -->
<img data-image-id="icon_123" alt="Reward Icon">
<!-- For background images -->
<div data-image-id="background_456" class="hero-section"></div>
Custom Font Injection¶
Elements with font data attributes automatically load custom fonts:
<div data-font-id="font_789" data-font-name="CustomFont" class="styled-text">
This text uses a custom font
</div>
Element Identification¶
Use data-id
for element identification:
<div data-id="player-info">
<span data-id="player-name">Player Name</span>
<span data-id="player-level">Level 1</span>
</div>
<script>
const playerInfo = balancy.findDomElement('player-info');
const nameElement = balancy.findDomElement('player-name', playerInfo);
</script>
Events System¶
Lifecycle Events¶
The framework dispatches these events during initialization:
balancy-buttons-complete
: All buttons are processedbalancy-localization-complete
: All text is localizedbalancy-text-complete
: All dynamic text is updatedbalancy-images-complete
: All images are loadedbalancy-fonts-complete
: All fonts are loadedbalancy-ready
: Everything is ready (dispatched after all above)
Example:
document.addEventListener('balancy-ready', () => {
console.log('Balancy UI is fully initialized');
// Your custom initialization code here
});
window.addEventListener('balancy-ready', main, { once: true });
Button Interaction Events¶
balancyButtonResponse Event:
button.addEventListener('balancyButtonResponse', (event) => {
const { actionId, result, success, senderId } = event.detail;
if (success) {
console.log(`Action ${actionId} succeeded:`, result);
} else {
console.log(`Action ${actionId} failed:`, event.detail.error);
}
});
balancyButtonError Event:
button.addEventListener('balancyButtonError', (event) => {
const { actionId, error, paramsAttr } = event.detail;
console.error(`Button error: ${error}`);
});
Backend Notifications¶
The framework automatically handles notifications from the backend:
// This is handled automatically, but you can override:
balancy.notificationReceived = function(data) {
console.log('Notification received:', data);
switch (data.type) {
case 101: // OnOfferDeactivated
if (window.balancyViewOwner.instanceId === data.id) {
balancy.closeView('Offer expired');
}
break;
case 122: // OnOfferGroupWasPurchased
// Refresh UI state
updateOfferAvailability();
break;
}
};
Global Variables¶
window.balancyViewOwner
¶
Contains context information about the current view:
{
instanceId: "offer_123", // Current offer/event instance ID
unnyIdGameEvent: "663", // Game event ID (for battle pass, etc.)
productId: "coin_pack_small" // Product ID for purchases
}
window.balancySettings
¶
Contains view settings and timing information:
{
launchTime: 1751328000, // Unix timestamp when view was launched
secondsLeft: 86400 // Seconds remaining for time-limited content
}
Custom Function Overrides¶
You can override certain behaviors:
// Custom time formatting
window.customFormatTime = function(seconds) {
if (seconds < 60) return `${seconds}s`;
if (seconds < 3600) return `${Math.floor(seconds/60)}m`;
return `${Math.floor(seconds/3600)}h`;
};
Common Patterns¶
Loading and Displaying Data¶
async function loadPlayerInfo() {
try {
const level = await balancy.getSystemProfileValue('GeneralInfo.Level');
const coins = await balancy.getProfileValue('Profile', 'Currency.Coins');
const playerName = await balancy.getProfileValue('Profile', 'GeneralInfo.Name');
document.getElementById('level').textContent = level;
document.getElementById('coins').textContent = coins;
document.getElementById('name').textContent = playerName;
} catch (error) {
console.error('Failed to load player info:', error);
}
}
Creating Interactive Buttons¶
function createOfferButton(offer) {
const button = document.createElement('button');
button.setAttribute('data-button-action', '101'); // BuyOffer
button.setAttribute('data-button-params', JSON.stringify({
productId: offer.productId,
currency: 'USD'
}));
button.addEventListener('balancyButtonResponse', (event) => {
if (event.detail.success) {
showPurchaseSuccess(offer);
} else {
showPurchaseError(event.detail.error);
}
});
return button;
}
Timer Implementation¶
function startTimer() {
function updateTimer() {
const timeLeft = balancy.getTimeLeft();
const formatted = balancy.formatTime(timeLeft);
document.getElementById('timer').textContent = formatted;
if (timeLeft <= 0) {
balancy.closeView('Time expired');
return;
}
}
updateTimer();
setInterval(updateTimer, 1000);
}
Handling Notifications¶
// Override notification handler for custom behavior
const originalNotificationHandler = balancy.notificationReceived;
balancy.notificationReceived = function(data) {
// Call original handler
originalNotificationHandler.call(this, data);
// Add custom logic
if (data.type === 122) { // OnOfferGroupWasPurchased
showCelebrationAnimation();
refreshOfferList();
}
};
Working with Images and Localization¶
async function setupRewardDisplay(reward) {
const container = document.getElementById('reward-container');
// Create image element
const img = document.createElement('img');
balancy.setImage(img, reward.item.unnyIcon);
// Create title with localization
const title = document.createElement('h3');
const localizedName = await balancy.getLocalizedText(reward.item.name);
title.textContent = localizedName;
// Create count display
const count = document.createElement('span');
count.textContent = `x${reward.count}`;
container.appendChild(img);
container.appendChild(title);
container.appendChild(count);
}
Battle Pass Integration¶
The Battle Pass is a complete example of advanced Balancy integration. Here's a simplified version showing key concepts:
Basic Setup¶
async function initializeBattlePass() {
// Delay automatic ready signal
balancy.delayIsReady();
try {
// Load configuration and progress
const config = await balancy.getBattlePassConfig();
const progress = await balancy.getBattlePassProgress();
// Setup UI
setupHeader(progress, config.scores);
setupRewardTrack(config.rewards, progress);
setupClaimButtons(config.rewards, progress);
// Notify ready
balancy.sendIsReady();
} catch (error) {
console.error('Failed to initialize battle pass:', error);
}
}
window.addEventListener('balancy-ready', initializeBattlePass, { once: true });
Progress Display¶
function updateProgressDisplay(progress, scores) {
const currentLevel = progress.level;
const currentScores = progress.scores;
const maxScores = scores[currentLevel] || scores[scores.length - 1];
const progressPercent = (currentScores / maxScores) * 100;
document.getElementById('level').textContent = currentLevel + 1;
document.getElementById('progress').style.width = `${progressPercent}%`;
document.getElementById('scores').textContent = `${currentScores} / ${maxScores}`;
}
Reward Claiming¶
function setupClaimButton(lineId, levelIndex, reward, status) {
const button = document.getElementById(`claim-${lineId}-${levelIndex}`);
if (status === 1) { // Available
button.style.display = 'block';
button.onclick = async () => {
const success = await balancy.claimBattlePassReward(lineId, levelIndex);
if (success) {
// Show reward animation
showRewardAnimation(button, reward);
// Update button state
setupClaimButton(lineId, levelIndex, reward, 2); // Claimed
}
};
} else {
button.style.display = 'none';
}
}
Animation System¶
function showRewardAnimation(sourceElement, reward) {
const flyingDiv = document.createElement('div');
flyingDiv.className = 'flying-reward';
// Position at source element
const rect = sourceElement.getBoundingClientRect();
flyingDiv.style.left = rect.left + 'px';
flyingDiv.style.top = rect.top + 'px';
// Add reward icon
const icon = document.createElement('img');
balancy.setImage(icon, reward.item.unnyIcon);
flyingDiv.appendChild(icon);
// Add to page and animate
document.body.appendChild(flyingDiv);
// Animate upward and fade out
flyingDiv.animate([
{ transform: 'translateY(0px)', opacity: 1 },
{ transform: 'translateY(-100px)', opacity: 0 }
], {
duration: 2000,
easing: 'ease-out'
}).addEventListener('finish', () => {
flyingDiv.remove();
});
}
This documentation provides a comprehensive guide to the Balancy UI Builder's JavaScript capabilities. The system is designed to be both powerful for complex implementations and simple for basic use cases through its declarative data attribute system.