Skip to content

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

  1. Introduction
  2. UI Builder
  3. JavaScript API Reference
  4. Data Attributes System
  5. Events System
  6. Global Variables
  7. Common Patterns
  8. 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:

UI Builder opening

In general, UI Builder will look like this:

UI Builder main layout

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:

UI Builder main layout

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 for
  • parentElement (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 name
  • path (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 ID
  • depth (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 ID
  • index (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} placeholders
  • data (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 enum
  • data-button-params: JSON string with additional parameters
  • data-index: Index parameter for certain actions

Button Events:

  • balancyButtonResponse: Fired when action completes
  • balancyButtonError: 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 updates
  • data-info-type: Type of information to fetch (InfoType enum)
  • data-text-format: Template string for formatting
  • data-custom: Custom parameter for InfoType.Custom
  • data-product-id: Product ID for price queries
  • data-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 processed
  • balancy-localization-complete: All text is localized
  • balancy-text-complete: All dynamic text is updated
  • balancy-images-complete: All images are loaded
  • balancy-fonts-complete: All fonts are loaded
  • balancy-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.