Menu

HTML Feedback Widget

New to BetaHub? BetaHub is a bug tracking and feedback platform for game developers. This widget adds a feedback button to your web game or application. Learn more →

A lightweight, embeddable feedback widget for web games and applications. Allow your users to submit bug reports, feature suggestions, and support tickets directly from your app with a beautiful, non-intrusive interface.

Try the Live Demo →

Overview

The BetaHub HTML Widget is a single-file JavaScript solution that adds a floating feedback button to your web game or application. When clicked, it opens a modal where users can report bugs, suggest features, or request support.

Perfect for:

  • Browser-based games (PixiJS, Phaser, Three.js, Babylon.js)
  • Unity WebGL exports
  • Web applications with player/user feedback needs
  • Any HTML5 project needing feedback collection

Key Benefits:

  • Zero Dependencies - Pure vanilla JavaScript, no libraries required
  • Isolated Styling - Uses Shadow DOM to prevent CSS conflicts with your game
  • Lightweight - Single file, minimal performance impact
  • Clean Design - Pastel blue light theme with professional, clean UI
  • Flexible Contact Options - 5 different modes for handling user emails
  • Custom Metadata - Attach game version, player level, and any contextual data

Quick Start

1. Include the Widget

Option A: CDN (Recommended - Fastest)

<script src="https://cdn.betahub.io/widget/latest/betahub-widget.js"></script>

Option B: Self-Hosted Download betahub-widget.js from the latest GitHub release and include it:

<script src="path/to/betahub-widget.js"></script>

Which should I use?

  • CDN: Faster setup, automatic updates, better caching, no downloads needed
  • Self-hosted: More control, works offline, no external dependency

2. Initialize the Widget

BetaHubWidget.init({
  projectId: 'your-project-id',
  authToken: 'tkn-your-auth-token'
});

That’s it! A feedback button will appear in the bottom-right corner of your page.

3. Get Your Credentials

  1. Go to your BetaHub project dashboard
  2. Navigate to Settings → Integrations → Auth Tokens tab
  3. Create a new auth token with these permissions:
    • can_create_bug_report
    • can_create_feature_request
    • can_create_support_ticket
  4. Copy your project ID (shown at top) and the generated token
  5. Use them in the widget initialization

Contact Information Modes

The widget offers five different ways to handle user contact information, allowing you to choose the approach that best fits your use case.

Quick Decision Guide

Your Situation Use This Mode Configuration
Public game, anonymous players Anonymous (Mode 1) Default - no email config
Closed beta, known testers Required Email (Mode 2) requireEmail: true
Users logged in, want transparency Prefilled Visible (Mode 3) userEmail + showEmailField: 'always'
Users logged in, prefer clean UI Prefilled Hidden (Mode 4) userEmail + showEmailField: 'never'
Not sure / mixed scenarios Prefilled Auto (Mode 5) userEmail only (default)

Still not sure? Start with Prefilled Auto (Mode 5) - it adapts intelligently based on whether users are logged in.

Detailed Mode Explanations

Mode 1: Anonymous (Default)

Best for public feedback where user identity is optional.

BetaHubWidget.init({
  projectId: 'pr-123',
  authToken: 'tkn-abc'
  // No email configuration
});

Behavior:

  • Bug Reports & Suggestions: No email field shown, submissions are anonymous
  • Support Tickets: Email field shown and required (support always needs contact info)

Use when:

  • You want to lower the barrier for feedback
  • User identity isn’t critical for bugs/suggestions
  • You have a public game with anonymous players

Mode 2: Required Email

Best for tracking all feedback to specific users.

BetaHubWidget.init({
  projectId: 'pr-123',
  authToken: 'tkn-abc',
  requireEmail: true
});

Behavior:

  • All Feedback Types: Email field shown and required

Use when:

  • You need to follow up on all feedback
  • You want to associate feedback with user accounts
  • You’re running a closed beta with known testers

Mode 3: Prefilled Visible Email

Best for logged-in users where email is known and you want transparency.

BetaHubWidget.init({
  projectId: 'pr-123',
  authToken: 'tkn-abc',
  userEmail: 'player@example.com',
  showEmailField: 'always'
});

Behavior:

  • All Feedback Types: Email shown as read-only (user sees but cannot edit)

Use when:

  • Users are logged into your game/app
  • You want users to see what email will be used
  • Transparency is important to your users

Mode 4: Prefilled Hidden Email

Best for silent user tracking without showing email in the UI.

BetaHubWidget.init({
  projectId: 'pr-123',
  authToken: 'tkn-abc',
  userEmail: 'player@example.com',
  showEmailField: 'never'
});

Behavior:

  • All Feedback Types: Email sent in authorization header but not visible in UI

Use when:

  • Users are logged in and you have their email
  • You want a cleaner UI without showing email
  • You want to track feedback by user without UI clutter

Mode 5: Prefilled Auto (Smart Default)

Best for most use cases - automatically shows email when provided.

BetaHubWidget.init({
  projectId: 'pr-123',
  authToken: 'tkn-abc',
  userEmail: 'player@example.com'
  // showEmailField defaults to 'auto'
});

Behavior:

  • All Feedback Types: Shows read-only email field automatically when userEmail is provided
  • If userEmail is not provided, behaves like Anonymous mode

Use when:

  • You want smart defaults without overthinking configuration
  • Some users are logged in, others aren’t
  • You want the widget to handle email display intelligently

How Virtual Users Work

When you provide userEmail, the widget:

  1. Sends the email in the authorization header: FormUser tkn-abc,email:user@example.com
  2. BetaHub creates or reuses a “virtual user” linked to that email
  3. All feedback from that email is grouped together in BetaHub
  4. Users can receive email notifications about their feedback updates
  5. You can search and filter feedback by user email in BetaHub

Note: Support tickets always require contact information. If using anonymous mode, users must manually enter their email for support tickets.

Configuration

Required Parameters

Parameter Type Description
projectId string Your BetaHub project ID (format: pr-...)
authToken string Authentication token with appropriate permissions (format: tkn-...)

Optional Parameters

Parameter Type Default Description
apiBaseUrl string 'https://app.betahub.io' BetaHub API endpoint (change for self-hosted instances)
releaseLabel string null Version label for bug reports (auto-creates release if doesn’t exist). If not provided, bugs are assigned to the project’s latest release
position string 'bottom-right' Button position: 'bottom-right', 'bottom-left', 'top-right', 'top-left'
buttonText string 'Feedback' Custom text for the floating button
userEmail string null Pre-filled user email (creates/links virtual user)
requireEmail boolean false Require email for bugs/suggestions (tickets always require email)
showEmailField string 'auto' Email field visibility: 'auto', 'always', 'never'
customFields object {} Custom metadata included with every submission

Full Configuration Example

BetaHubWidget.init({
  // Required
  projectId: 'pr-1234567890',
  authToken: 'tkn-your-auth-token-here',

  // Optional - API & UI
  apiBaseUrl: 'https://app.betahub.io',
  releaseLabel: '1.2.3',  // Version label (auto-creates release if not exists)
  position: 'bottom-left',
  buttonText: '🐛 Report Bug',

  // Optional - Contact Information
  userEmail: 'player@example.com',
  requireEmail: false,
  showEmailField: 'auto',

  // Optional - Custom Metadata
  customFields: {
    platform: 'web',
    playerLevel: 15,
    currentScene: 'battle-arena',
    sessionId: 'abc-123-def-456',
    buildType: 'release'
  }
});

These custom fields are automatically included with every bug report, feature request, and support ticket submission. Note that custom fields must be predefined in your BetaHub project settings (Settings → Custom Fields) and marked as “Tester Settable” to accept values from the widget.

Feedback Types

The widget supports three types of feedback:

Bug Reports

For crashes, errors, and unexpected behavior.

Required Fields:

  • Description (max 2000 characters)
  • Steps to Reproduce (max 1000 characters)
  • Email (only if requireEmail: true or using prefilled email)

API Endpoint: POST /projects/{projectId}/issues.json

Best Practices:

  • Encourage users to be specific about what happened
  • Steps to reproduce help developers fix bugs faster
  • Automatic metadata (game version, scene, etc.) provides critical context

Feature Suggestions

For feature requests, improvements, and ideas.

Required Fields:

  • Description (max 2000 characters)
  • Email (only if requireEmail: true or using prefilled email)

API Endpoint: POST /projects/{projectId}/feature_requests.json

Best Practices:

  • Let users explain why they want the feature
  • Custom metadata helps prioritize features by player segment
  • Anonymous suggestions encourage more honest feedback

Support Tickets

For help requests, questions, and general support.

Required Fields:

  • Description (max 2000 characters)
  • Email (always required - support needs contact info)

API Endpoint: POST /projects/{projectId}/tickets.json

Best Practices:

  • Support always requires email for follow-up
  • If using anonymous mode, users must manually enter email
  • If using prefilled email modes, email is automatically included

Game Engine Integration

PixiJS

Try the PixiJS Live Demo →

// Add to your game initialization
const app = new PIXI.Application();

// Initialize BetaHub widget
BetaHubWidget.init({
  projectId: 'your-project-id',
  authToken: 'tkn-your-token',
  userEmail: currentUser.email, // If user is logged in
  customFields: {
    gameVersion: '1.0.0',
    renderer: app.renderer.type === PIXI.RENDERER_TYPE.WEBGL ? 'WebGL' : 'Canvas',
    fps: app.ticker.FPS.toFixed(2)
  }
});

Phaser

// In your game's create() function
class GameScene extends Phaser.Scene {
  create() {
    // Your game setup...

    // Initialize BetaHub widget
    BetaHubWidget.init({
      projectId: 'your-project-id',
      authToken: 'tkn-your-token',
      customFields: {
        gameVersion: '1.0.0',
        scene: this.scene.sys.settings.key, // Access scene key via sys.settings
        worldTime: this.time.now // Milliseconds since scene started
      }
    });
  }
}

Unity WebGL

<!-- In your Unity WebGL HTML template (Unity 2020.1+) -->
<!DOCTYPE html>
<html>
<head>
  <!-- BetaHub widget - include via CDN or self-hosted -->
  <script src="https://cdn.betahub.io/widget/latest/betahub-widget.js"></script>
</head>
<body>
  <canvas id="unity-canvas"></canvas>
  <script src="Build/build.loader.js"></script>
  <script>
    const canvas = document.querySelector("#unity-canvas");
    const config = {
      dataUrl: "Build/build.data",
      frameworkUrl: "Build/build.framework.js",
      codeUrl: "Build/build.wasm",
    };

    // Initialize Unity with loading progress callback
    createUnityInstance(canvas, config, (progress) => {
      console.log(`Loading: ${Math.round(progress * 100)}%`);
    }).then((unityInstance) => {
      console.log("Unity loaded successfully");

      // Initialize BetaHub after Unity loads
      BetaHubWidget.init({
        projectId: 'your-project-id',
        authToken: 'tkn-your-token',
        customFields: {
          unityVersion: unityInstance.Module.unityVersion || 'unknown',
          platform: 'webgl',
          buildType: 'release'
        }
      });
    }).catch((error) => {
      console.error("Unity failed to load:", error);
    });
  </script>
</body>
</html>

Note: Unity 2020.1+ uses a different loader API than earlier versions. If you’re using Unity 2019 or earlier, refer to the Unity documentation for the appropriate loader syntax.

Three.js

// After setting up your Three.js scene
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();

// Initialize BetaHub widget
BetaHubWidget.init({
  projectId: 'your-project-id',
  authToken: 'tkn-your-token',
  customFields: {
    gameVersion: '1.0.0',
    renderer: 'WebGL',
    drawCalls: renderer.info.render.calls,
    triangles: renderer.info.render.triangles
  }
});

React

import { useEffect } from 'react';

function App() {
  useEffect(() => {
    // Load widget script
    const script = document.createElement('script');
    script.src = '/betahub-widget.js';
    script.onload = () => {
      window.BetaHubWidget.init({
        projectId: 'your-project-id',
        authToken: 'tkn-your-token',
        userEmail: currentUser?.email, // From your auth system
        customFields: {
          appVersion: '1.0.0',
          environment: process.env.NODE_ENV,
          userId: currentUser?.id
        }
      });
    };
    document.body.appendChild(script);

    return () => {
      // Cleanup - remove script and widget container
      if (document.body.contains(script)) {
        document.body.removeChild(script);
      }
      const container = document.querySelector('#betahub-widget-container');
      if (container && document.body.contains(container)) {
        document.body.removeChild(container);
      }
    };
  }, []);

  return <div>Your App</div>;
}

Programmatic API

The widget provides two main programmatic methods for runtime control:

Opening the Widget

You can open the widget programmatically from your code:

// Open the feedback widget
BetaHubWidget.open();

Use Cases:

Custom Feedback Buttons:

document.getElementById('report-bug-btn').addEventListener('click', function() {
  BetaHubWidget.open();
});

Keyboard Shortcuts:

document.addEventListener('keydown', function(e) {
  // Press F1 to open feedback
  if (e.key === 'F1') {
    e.preventDefault();
    BetaHubWidget.open();
  }
});

Updating Custom Fields Dynamically

Update custom fields after initialization to capture real-time game state when feedback is submitted:

// Update custom fields as game state changes
BetaHubWidget.updateCustomFields({
  level: '5',
  score: '1250',
  health: '75'
});

This method is particularly useful for:

  • Capturing current game state (level, score, health) when feedback is submitted
  • Updating player progress dynamically during gameplay
  • Including real-time context with bug reports without re-initializing the widget

How it works: updateCustomFields() merges new fields with existing customFields (doesn’t replace them). Call this method anytime after init() to update values. Updated fields will be included in the next feedback submission.

Example: Dynamic Game State Tracking

// Initialize once at game start
BetaHubWidget.init({
  projectId: 'your-project-id',
  authToken: 'tkn-your-token',
  customFields: {
    gameVersion: '1.0.0',
    platform: 'web'
  }
});

// Update dynamically as player progresses
function onLevelChange(newLevel) {
  BetaHubWidget.updateCustomFields({
    currentLevel: newLevel.toString(),
    levelStartTime: Date.now()
  });
}

function onScoreUpdate(newScore) {
  BetaHubWidget.updateCustomFields({
    score: newScore.toString()
  });
}

function onHealthChange(newHealth) {
  BetaHubWidget.updateCustomFields({
    health: newHealth.toString()
  });
}

Example: Context-Specific Feedback

// When player encounters an error in a specific level
function onLevelError(levelId, errorType) {
  // Update custom fields with error context
  BetaHubWidget.updateCustomFields({
    errorLevel: levelId,
    errorType: errorType,
    errorTimestamp: Date.now()
  });

  // Open widget for user to report the issue
  BetaHubWidget.open();
}

Notes:

  • All custom field values should be strings for consistency
  • Fields are merged, not replaced - existing fields remain unless overwritten
  • Custom fields must be predefined in BetaHub project settings (Settings → Custom Fields) and marked as “Tester Settable”

Custom Metadata

Custom fields allow you to attach contextual data to all feedback submissions (bug reports, feature requests, and support tickets). This is incredibly powerful for debugging and prioritizing feedback.

Setup Required: Custom fields must be created in your BetaHub project settings (Settings → Custom Fields) and marked as “Tester Settable” before the widget can use them. See game engine integration guide for step-by-step instructions.

Version Labels vs Custom Fields: Use the releaseLabel parameter to assign bug reports to specific releases in BetaHub (e.g., releaseLabel: '1.2.3'). This creates or assigns to a release, which you can track separately. If releaseLabel is not provided, bug reports are automatically assigned to your project’s latest release. Custom fields like buildNumber or buildType are additional metadata for your own tracking purposes.

Common Use Cases

Game Context:

customFields: {
  // Build tracking (metadata - different from releaseLabel)
  buildNumber: '2025.11.01',
  buildType: 'release',  // 'debug', 'release', 'beta'

  // Player context
  playerLevel: 15,
  playerClass: 'warrior',
  playTime: 3600, // seconds

  // Game state
  currentScene: 'battle-arena',
  difficulty: 'hard',
  checkpoint: 'boss-3',

  // Technical info
  platform: 'web',
  browser: navigator.userAgent,
  sessionId: 'unique-session-id'
}

Performance Metrics:

customFields: {
  fps: currentFPS,
  memoryUsed: performance.memory?.usedJSHeapSize,
  loadTime: window.performance.timing.loadEventEnd - window.performance.timing.navigationStart
}

A/B Testing:

customFields: {
  experimentGroup: 'variant-b',
  featureFlags: JSON.stringify(activeFeatureFlags)
}

Viewing Custom Fields in BetaHub

All custom fields appear in the bug report details page in BetaHub, allowing you to:

  • Filter and search by custom field values
  • See patterns across similar bugs (e.g., all bugs on level 5)
  • Prioritize based on affected player segments
  • Debug with full context of the game state

Customization

Button Position

Position the feedback button in any corner:

BetaHubWidget.init({
  projectId: 'your-project-id',
  authToken: 'tkn-your-token',
  position: 'bottom-left' // or 'top-right', 'top-left', 'bottom-right'
});

Button Text

Customize the button label:

BetaHubWidget.init({
  projectId: 'your-project-id',
  authToken: 'tkn-your-token',
  buttonText: '🐛 Report Issue'
});

Color Palette

The widget uses a fixed pastel blue light theme. To customize colors, edit the getStyles() method in betahub-widget.js:

// Find and replace these colors:

// Primary brand color
background: #237390; // Change to your brand color

// Button hover state
background: #1E627B; // Darker shade of your brand color

// Button active state
background: #144252; // Even darker shade

Color Guidelines:

  • Maintain sufficient contrast for accessibility (WCAG AA: 4.5:1 minimum)
  • Test hover and active states for visibility
  • Keep semantic colors (success, error, warning) distinct from brand colors

Browser Compatibility

The widget requires browsers with Shadow DOM support:

  • Chrome: 53+
  • Firefox: 63+
  • Safari: 10.1+
  • Edge: 79+
  • Opera: 40+

These browsers cover 95%+ of modern web users. For older browsers, the widget will gracefully fail to load without breaking your game.

Security

Token Security

Best Practices:

  • Never commit auth tokens to version control
  • Use environment-specific tokens (dev/staging/production)
  • Rotate tokens regularly from the BetaHub dashboard
  • Use different tokens for different environments

Example - Environment Variables:

BetaHubWidget.init({
  projectId: process.env.BETAHUB_PROJECT_ID,
  authToken: process.env.BETAHUB_AUTH_TOKEN
});

Rate Limiting

To prevent abuse, BetaHub enforces rate limits:

  • Default: 8 submissions per day per feedback type per IP address
  • Customizable: Adjust limits in your BetaHub project settings
  • Behavior: When limit is reached, users see a friendly error message

CORS Configuration

The widget uses CORS for API requests. BetaHub automatically allows requests from:

  • Any origin (for public projects)
  • Whitelisted domains (configurable in project settings)

If you encounter CORS errors:

  1. Check your project CORS settings in BetaHub
  2. Ensure you’re using HTTPS (HTTP may be blocked)
  3. Verify your domain is whitelisted if using domain restrictions

Troubleshooting

Widget Not Appearing

Check Console for Errors:

// Open browser console (F12) and look for errors

Common Causes:

  • Invalid projectId or authToken
  • Script not loaded (check network tab)
  • JavaScript errors in your game blocking widget initialization

Solution:

// Test with a minimal setup first
BetaHubWidget.init({
  projectId: 'pr-your-id',
  authToken: 'tkn-your-token'
});

Submissions Not Arriving

Check Network Tab:

  • Look for failed POST requests to BetaHub API
  • Check response status codes

Common Causes:

  • Invalid auth token or expired token
  • Missing required permissions on token
  • CORS issues (see CORS section above)
  • Rate limit exceeded

Solution:

  1. Verify token has correct permissions in BetaHub dashboard
  2. Check rate limits in project settings
  3. Test with a fresh token

Styling Conflicts

The widget uses Shadow DOM to prevent CSS conflicts, but if you experience issues:

Common Causes:

  • Global CSS overriding Shadow DOM (rare)
  • z-index conflicts with your game UI

Solution:

/* If the button is hidden behind your game */
#betahub-widget-container {
  z-index: 10000 !important;
}

Email Field Not Showing/Hiding Correctly

Check Configuration:

// Mode 1: Anonymous (no email for bugs/suggestions)
BetaHubWidget.init({ projectId: '...', authToken: '...' });

// Mode 2: Required (email required for all)
BetaHubWidget.init({ projectId: '...', authToken: '...', requireEmail: true });

// Mode 3: Prefilled visible
BetaHubWidget.init({ projectId: '...', authToken: '...', userEmail: 'user@example.com', showEmailField: 'always' });

// Mode 4: Prefilled hidden
BetaHubWidget.init({ projectId: '...', authToken: '...', userEmail: 'user@example.com', showEmailField: 'never' });

// Mode 5: Prefilled auto (smart default)
BetaHubWidget.init({ projectId: '...', authToken: '...', userEmail: 'user@example.com' });

Remember: Support tickets always require email, regardless of configuration.

Advanced Usage

Dynamic Configuration Updates

You can reinitialize the widget with different settings:

// Initial setup
BetaHubWidget.init({
  projectId: 'your-project-id',
  authToken: 'tkn-your-token'
});

// Later, when user logs in
BetaHubWidget.init({
  projectId: 'your-project-id',
  authToken: 'tkn-your-token',
  userEmail: loggedInUser.email,
  customFields: {
    userId: loggedInUser.id,
    playerLevel: loggedInUser.level
  }
});

Note: Calling init() multiple times may create duplicate widget instances. For single-page applications, initialize the widget once and avoid re-initialization unless necessary. If you need to update custom fields dynamically, use the updateCustomFields() method instead of re-initializing.

Conditional Loading

Only load the widget in specific environments:

// Only in production
if (process.env.NODE_ENV === 'production') {
  BetaHubWidget.init({
    projectId: 'your-project-id',
    authToken: 'tkn-your-token'
  });
}

// Only for beta testers
if (user.isBetaTester) {
  BetaHubWidget.init({
    projectId: 'your-project-id',
    authToken: 'tkn-your-token',
    userEmail: user.email
  });
}

Tracking Submission Success

The widget doesn’t expose submission callbacks, but you can monitor network requests:

// Listen for successful submissions
window.addEventListener('betahub-submission-success', function(e) {
  console.log('Feedback submitted successfully!');
  // Show custom thank you message
});

Note: This event is not yet implemented in the current version but is planned for future releases.

File Structure

betahub-html-widget/
├── betahub-widget.js           # Main widget (single file, zero dependencies)
├── demo.html                   # Live demo with documentation
└── README.md                   # Technical documentation

Next Steps

Getting Help

For issues with the HTML widget:


The HTML Feedback Widget is open source and contributions are welcome! Visit the GitHub repository to contribute.

HTML Feedback Widget

New to BetaHub? BetaHub is a bug tracking and feedback platform for game developers. This widget adds a feedback button to your web game or application. Learn more →

A lightweight, embeddable feedback widget for web games and applications. Allow your users to submit bug reports, feature suggestions, and support tickets directly from your app with a beautiful, non-intrusive interface.

Try the Live Demo →

Overview

The BetaHub HTML Widget is a single-file JavaScript solution that adds a floating feedback button to your web game or application. When clicked, it opens a modal where users can report bugs, suggest features, or request support.

Perfect for:

  • Browser-based games (PixiJS, Phaser, Three.js, Babylon.js)
  • Unity WebGL exports
  • Web applications with player/user feedback needs
  • Any HTML5 project needing feedback collection

Key Benefits:

  • Zero Dependencies - Pure vanilla JavaScript, no libraries required
  • Isolated Styling - Uses Shadow DOM to prevent CSS conflicts with your game
  • Lightweight - Single file, minimal performance impact
  • Clean Design - Pastel blue light theme with professional, clean UI
  • Flexible Contact Options - 5 different modes for handling user emails
  • Custom Metadata - Attach game version, player level, and any contextual data

Quick Start

1. Include the Widget

Option A: CDN (Recommended - Fastest)

<script src="https://cdn.betahub.io/widget/latest/betahub-widget.js"></script>

Option B: Self-Hosted Download betahub-widget.js from the latest GitHub release and include it:

<script src="path/to/betahub-widget.js"></script>

Which should I use?

  • CDN: Faster setup, automatic updates, better caching, no downloads needed
  • Self-hosted: More control, works offline, no external dependency

2. Initialize the Widget

BetaHubWidget.init({
  projectId: 'your-project-id',
  authToken: 'tkn-your-auth-token'
});

That’s it! A feedback button will appear in the bottom-right corner of your page.

3. Get Your Credentials

  1. Go to your BetaHub project dashboard
  2. Navigate to Settings → Integrations → Auth Tokens tab
  3. Create a new auth token with these permissions:
    • can_create_bug_report
    • can_create_feature_request
    • can_create_support_ticket
  4. Copy your project ID (shown at top) and the generated token
  5. Use them in the widget initialization

Contact Information Modes

The widget offers five different ways to handle user contact information, allowing you to choose the approach that best fits your use case.

Quick Decision Guide

Your Situation Use This Mode Configuration
Public game, anonymous players Anonymous (Mode 1) Default - no email config
Closed beta, known testers Required Email (Mode 2) requireEmail: true
Users logged in, want transparency Prefilled Visible (Mode 3) userEmail + showEmailField: 'always'
Users logged in, prefer clean UI Prefilled Hidden (Mode 4) userEmail + showEmailField: 'never'
Not sure / mixed scenarios Prefilled Auto (Mode 5) userEmail only (default)

Still not sure? Start with Prefilled Auto (Mode 5) - it adapts intelligently based on whether users are logged in.

Detailed Mode Explanations

Mode 1: Anonymous (Default)

Best for public feedback where user identity is optional.

BetaHubWidget.init({
  projectId: 'pr-123',
  authToken: 'tkn-abc'
  // No email configuration
});

Behavior:

  • Bug Reports & Suggestions: No email field shown, submissions are anonymous
  • Support Tickets: Email field shown and required (support always needs contact info)

Use when:

  • You want to lower the barrier for feedback
  • User identity isn’t critical for bugs/suggestions
  • You have a public game with anonymous players

Mode 2: Required Email

Best for tracking all feedback to specific users.

BetaHubWidget.init({
  projectId: 'pr-123',
  authToken: 'tkn-abc',
  requireEmail: true
});

Behavior:

  • All Feedback Types: Email field shown and required

Use when:

  • You need to follow up on all feedback
  • You want to associate feedback with user accounts
  • You’re running a closed beta with known testers

Mode 3: Prefilled Visible Email

Best for logged-in users where email is known and you want transparency.

BetaHubWidget.init({
  projectId: 'pr-123',
  authToken: 'tkn-abc',
  userEmail: 'player@example.com',
  showEmailField: 'always'
});

Behavior:

  • All Feedback Types: Email shown as read-only (user sees but cannot edit)

Use when:

  • Users are logged into your game/app
  • You want users to see what email will be used
  • Transparency is important to your users

Mode 4: Prefilled Hidden Email

Best for silent user tracking without showing email in the UI.

BetaHubWidget.init({
  projectId: 'pr-123',
  authToken: 'tkn-abc',
  userEmail: 'player@example.com',
  showEmailField: 'never'
});

Behavior:

  • All Feedback Types: Email sent in authorization header but not visible in UI

Use when:

  • Users are logged in and you have their email
  • You want a cleaner UI without showing email
  • You want to track feedback by user without UI clutter

Mode 5: Prefilled Auto (Smart Default)

Best for most use cases - automatically shows email when provided.

BetaHubWidget.init({
  projectId: 'pr-123',
  authToken: 'tkn-abc',
  userEmail: 'player@example.com'
  // showEmailField defaults to 'auto'
});

Behavior:

  • All Feedback Types: Shows read-only email field automatically when userEmail is provided
  • If userEmail is not provided, behaves like Anonymous mode

Use when:

  • You want smart defaults without overthinking configuration
  • Some users are logged in, others aren’t
  • You want the widget to handle email display intelligently

How Virtual Users Work

When you provide userEmail, the widget:

  1. Sends the email in the authorization header: FormUser tkn-abc,email:user@example.com
  2. BetaHub creates or reuses a “virtual user” linked to that email
  3. All feedback from that email is grouped together in BetaHub
  4. Users can receive email notifications about their feedback updates
  5. You can search and filter feedback by user email in BetaHub

Note: Support tickets always require contact information. If using anonymous mode, users must manually enter their email for support tickets.

Configuration

Required Parameters

Parameter Type Description
projectId string Your BetaHub project ID (format: pr-...)
authToken string Authentication token with appropriate permissions (format: tkn-...)

Optional Parameters

Parameter Type Default Description
apiBaseUrl string 'https://app.betahub.io' BetaHub API endpoint (change for self-hosted instances)
releaseLabel string null Version label for bug reports (auto-creates release if doesn’t exist). If not provided, bugs are assigned to the project’s latest release
position string 'bottom-right' Button position: 'bottom-right', 'bottom-left', 'top-right', 'top-left'
buttonText string 'Feedback' Custom text for the floating button
userEmail string null Pre-filled user email (creates/links virtual user)
requireEmail boolean false Require email for bugs/suggestions (tickets always require email)
showEmailField string 'auto' Email field visibility: 'auto', 'always', 'never'
customFields object {} Custom metadata included with every submission

Full Configuration Example

BetaHubWidget.init({
  // Required
  projectId: 'pr-1234567890',
  authToken: 'tkn-your-auth-token-here',

  // Optional - API & UI
  apiBaseUrl: 'https://app.betahub.io',
  releaseLabel: '1.2.3',  // Version label (auto-creates release if not exists)
  position: 'bottom-left',
  buttonText: '🐛 Report Bug',

  // Optional - Contact Information
  userEmail: 'player@example.com',
  requireEmail: false,
  showEmailField: 'auto',

  // Optional - Custom Metadata
  customFields: {
    platform: 'web',
    playerLevel: 15,
    currentScene: 'battle-arena',
    sessionId: 'abc-123-def-456',
    buildType: 'release'
  }
});

These custom fields are automatically included with every bug report, feature request, and support ticket submission. Note that custom fields must be predefined in your BetaHub project settings (Settings → Custom Fields) and marked as “Tester Settable” to accept values from the widget.

Feedback Types

The widget supports three types of feedback:

Bug Reports

For crashes, errors, and unexpected behavior.

Required Fields:

  • Description (max 2000 characters)
  • Steps to Reproduce (max 1000 characters)
  • Email (only if requireEmail: true or using prefilled email)

API Endpoint: POST /projects/{projectId}/issues.json

Best Practices:

  • Encourage users to be specific about what happened
  • Steps to reproduce help developers fix bugs faster
  • Automatic metadata (game version, scene, etc.) provides critical context

Feature Suggestions

For feature requests, improvements, and ideas.

Required Fields:

  • Description (max 2000 characters)
  • Email (only if requireEmail: true or using prefilled email)

API Endpoint: POST /projects/{projectId}/feature_requests.json

Best Practices:

  • Let users explain why they want the feature
  • Custom metadata helps prioritize features by player segment
  • Anonymous suggestions encourage more honest feedback

Support Tickets

For help requests, questions, and general support.

Required Fields:

  • Description (max 2000 characters)
  • Email (always required - support needs contact info)

API Endpoint: POST /projects/{projectId}/tickets.json

Best Practices:

  • Support always requires email for follow-up
  • If using anonymous mode, users must manually enter email
  • If using prefilled email modes, email is automatically included

Game Engine Integration

PixiJS

Try the PixiJS Live Demo →

// Add to your game initialization
const app = new PIXI.Application();

// Initialize BetaHub widget
BetaHubWidget.init({
  projectId: 'your-project-id',
  authToken: 'tkn-your-token',
  userEmail: currentUser.email, // If user is logged in
  customFields: {
    gameVersion: '1.0.0',
    renderer: app.renderer.type === PIXI.RENDERER_TYPE.WEBGL ? 'WebGL' : 'Canvas',
    fps: app.ticker.FPS.toFixed(2)
  }
});

Phaser

// In your game's create() function
class GameScene extends Phaser.Scene {
  create() {
    // Your game setup...

    // Initialize BetaHub widget
    BetaHubWidget.init({
      projectId: 'your-project-id',
      authToken: 'tkn-your-token',
      customFields: {
        gameVersion: '1.0.0',
        scene: this.scene.sys.settings.key, // Access scene key via sys.settings
        worldTime: this.time.now // Milliseconds since scene started
      }
    });
  }
}

Unity WebGL

<!-- In your Unity WebGL HTML template (Unity 2020.1+) -->
<!DOCTYPE html>
<html>
<head>
  <!-- BetaHub widget - include via CDN or self-hosted -->
  <script src="https://cdn.betahub.io/widget/latest/betahub-widget.js"></script>
</head>
<body>
  <canvas id="unity-canvas"></canvas>
  <script src="Build/build.loader.js"></script>
  <script>
    const canvas = document.querySelector("#unity-canvas");
    const config = {
      dataUrl: "Build/build.data",
      frameworkUrl: "Build/build.framework.js",
      codeUrl: "Build/build.wasm",
    };

    // Initialize Unity with loading progress callback
    createUnityInstance(canvas, config, (progress) => {
      console.log(`Loading: ${Math.round(progress * 100)}%`);
    }).then((unityInstance) => {
      console.log("Unity loaded successfully");

      // Initialize BetaHub after Unity loads
      BetaHubWidget.init({
        projectId: 'your-project-id',
        authToken: 'tkn-your-token',
        customFields: {
          unityVersion: unityInstance.Module.unityVersion || 'unknown',
          platform: 'webgl',
          buildType: 'release'
        }
      });
    }).catch((error) => {
      console.error("Unity failed to load:", error);
    });
  </script>
</body>
</html>

Note: Unity 2020.1+ uses a different loader API than earlier versions. If you’re using Unity 2019 or earlier, refer to the Unity documentation for the appropriate loader syntax.

Three.js

// After setting up your Three.js scene
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();

// Initialize BetaHub widget
BetaHubWidget.init({
  projectId: 'your-project-id',
  authToken: 'tkn-your-token',
  customFields: {
    gameVersion: '1.0.0',
    renderer: 'WebGL',
    drawCalls: renderer.info.render.calls,
    triangles: renderer.info.render.triangles
  }
});

React

import { useEffect } from 'react';

function App() {
  useEffect(() => {
    // Load widget script
    const script = document.createElement('script');
    script.src = '/betahub-widget.js';
    script.onload = () => {
      window.BetaHubWidget.init({
        projectId: 'your-project-id',
        authToken: 'tkn-your-token',
        userEmail: currentUser?.email, // From your auth system
        customFields: {
          appVersion: '1.0.0',
          environment: process.env.NODE_ENV,
          userId: currentUser?.id
        }
      });
    };
    document.body.appendChild(script);

    return () => {
      // Cleanup - remove script and widget container
      if (document.body.contains(script)) {
        document.body.removeChild(script);
      }
      const container = document.querySelector('#betahub-widget-container');
      if (container && document.body.contains(container)) {
        document.body.removeChild(container);
      }
    };
  }, []);

  return <div>Your App</div>;
}

Programmatic API

The widget provides two main programmatic methods for runtime control:

Opening the Widget

You can open the widget programmatically from your code:

// Open the feedback widget
BetaHubWidget.open();

Use Cases:

Custom Feedback Buttons:

document.getElementById('report-bug-btn').addEventListener('click', function() {
  BetaHubWidget.open();
});

Keyboard Shortcuts:

document.addEventListener('keydown', function(e) {
  // Press F1 to open feedback
  if (e.key === 'F1') {
    e.preventDefault();
    BetaHubWidget.open();
  }
});

Updating Custom Fields Dynamically

Update custom fields after initialization to capture real-time game state when feedback is submitted:

// Update custom fields as game state changes
BetaHubWidget.updateCustomFields({
  level: '5',
  score: '1250',
  health: '75'
});

This method is particularly useful for:

  • Capturing current game state (level, score, health) when feedback is submitted
  • Updating player progress dynamically during gameplay
  • Including real-time context with bug reports without re-initializing the widget

How it works: updateCustomFields() merges new fields with existing customFields (doesn’t replace them). Call this method anytime after init() to update values. Updated fields will be included in the next feedback submission.

Example: Dynamic Game State Tracking

// Initialize once at game start
BetaHubWidget.init({
  projectId: 'your-project-id',
  authToken: 'tkn-your-token',
  customFields: {
    gameVersion: '1.0.0',
    platform: 'web'
  }
});

// Update dynamically as player progresses
function onLevelChange(newLevel) {
  BetaHubWidget.updateCustomFields({
    currentLevel: newLevel.toString(),
    levelStartTime: Date.now()
  });
}

function onScoreUpdate(newScore) {
  BetaHubWidget.updateCustomFields({
    score: newScore.toString()
  });
}

function onHealthChange(newHealth) {
  BetaHubWidget.updateCustomFields({
    health: newHealth.toString()
  });
}

Example: Context-Specific Feedback

// When player encounters an error in a specific level
function onLevelError(levelId, errorType) {
  // Update custom fields with error context
  BetaHubWidget.updateCustomFields({
    errorLevel: levelId,
    errorType: errorType,
    errorTimestamp: Date.now()
  });

  // Open widget for user to report the issue
  BetaHubWidget.open();
}

Notes:

  • All custom field values should be strings for consistency
  • Fields are merged, not replaced - existing fields remain unless overwritten
  • Custom fields must be predefined in BetaHub project settings (Settings → Custom Fields) and marked as “Tester Settable”

Custom Metadata

Custom fields allow you to attach contextual data to all feedback submissions (bug reports, feature requests, and support tickets). This is incredibly powerful for debugging and prioritizing feedback.

Setup Required: Custom fields must be created in your BetaHub project settings (Settings → Custom Fields) and marked as “Tester Settable” before the widget can use them. See game engine integration guide for step-by-step instructions.

Version Labels vs Custom Fields: Use the releaseLabel parameter to assign bug reports to specific releases in BetaHub (e.g., releaseLabel: '1.2.3'). This creates or assigns to a release, which you can track separately. If releaseLabel is not provided, bug reports are automatically assigned to your project’s latest release. Custom fields like buildNumber or buildType are additional metadata for your own tracking purposes.

Common Use Cases

Game Context:

customFields: {
  // Build tracking (metadata - different from releaseLabel)
  buildNumber: '2025.11.01',
  buildType: 'release',  // 'debug', 'release', 'beta'

  // Player context
  playerLevel: 15,
  playerClass: 'warrior',
  playTime: 3600, // seconds

  // Game state
  currentScene: 'battle-arena',
  difficulty: 'hard',
  checkpoint: 'boss-3',

  // Technical info
  platform: 'web',
  browser: navigator.userAgent,
  sessionId: 'unique-session-id'
}

Performance Metrics:

customFields: {
  fps: currentFPS,
  memoryUsed: performance.memory?.usedJSHeapSize,
  loadTime: window.performance.timing.loadEventEnd - window.performance.timing.navigationStart
}

A/B Testing:

customFields: {
  experimentGroup: 'variant-b',
  featureFlags: JSON.stringify(activeFeatureFlags)
}

Viewing Custom Fields in BetaHub

All custom fields appear in the bug report details page in BetaHub, allowing you to:

  • Filter and search by custom field values
  • See patterns across similar bugs (e.g., all bugs on level 5)
  • Prioritize based on affected player segments
  • Debug with full context of the game state

Customization

Button Position

Position the feedback button in any corner:

BetaHubWidget.init({
  projectId: 'your-project-id',
  authToken: 'tkn-your-token',
  position: 'bottom-left' // or 'top-right', 'top-left', 'bottom-right'
});

Button Text

Customize the button label:

BetaHubWidget.init({
  projectId: 'your-project-id',
  authToken: 'tkn-your-token',
  buttonText: '🐛 Report Issue'
});

Color Palette

The widget uses a fixed pastel blue light theme. To customize colors, edit the getStyles() method in betahub-widget.js:

// Find and replace these colors:

// Primary brand color
background: #237390; // Change to your brand color

// Button hover state
background: #1E627B; // Darker shade of your brand color

// Button active state
background: #144252; // Even darker shade

Color Guidelines:

  • Maintain sufficient contrast for accessibility (WCAG AA: 4.5:1 minimum)
  • Test hover and active states for visibility
  • Keep semantic colors (success, error, warning) distinct from brand colors

Browser Compatibility

The widget requires browsers with Shadow DOM support:

  • Chrome: 53+
  • Firefox: 63+
  • Safari: 10.1+
  • Edge: 79+
  • Opera: 40+

These browsers cover 95%+ of modern web users. For older browsers, the widget will gracefully fail to load without breaking your game.

Security

Token Security

Best Practices:

  • Never commit auth tokens to version control
  • Use environment-specific tokens (dev/staging/production)
  • Rotate tokens regularly from the BetaHub dashboard
  • Use different tokens for different environments

Example - Environment Variables:

BetaHubWidget.init({
  projectId: process.env.BETAHUB_PROJECT_ID,
  authToken: process.env.BETAHUB_AUTH_TOKEN
});

Rate Limiting

To prevent abuse, BetaHub enforces rate limits:

  • Default: 8 submissions per day per feedback type per IP address
  • Customizable: Adjust limits in your BetaHub project settings
  • Behavior: When limit is reached, users see a friendly error message

CORS Configuration

The widget uses CORS for API requests. BetaHub automatically allows requests from:

  • Any origin (for public projects)
  • Whitelisted domains (configurable in project settings)

If you encounter CORS errors:

  1. Check your project CORS settings in BetaHub
  2. Ensure you’re using HTTPS (HTTP may be blocked)
  3. Verify your domain is whitelisted if using domain restrictions

Troubleshooting

Widget Not Appearing

Check Console for Errors:

// Open browser console (F12) and look for errors

Common Causes:

  • Invalid projectId or authToken
  • Script not loaded (check network tab)
  • JavaScript errors in your game blocking widget initialization

Solution:

// Test with a minimal setup first
BetaHubWidget.init({
  projectId: 'pr-your-id',
  authToken: 'tkn-your-token'
});

Submissions Not Arriving

Check Network Tab:

  • Look for failed POST requests to BetaHub API
  • Check response status codes

Common Causes:

  • Invalid auth token or expired token
  • Missing required permissions on token
  • CORS issues (see CORS section above)
  • Rate limit exceeded

Solution:

  1. Verify token has correct permissions in BetaHub dashboard
  2. Check rate limits in project settings
  3. Test with a fresh token

Styling Conflicts

The widget uses Shadow DOM to prevent CSS conflicts, but if you experience issues:

Common Causes:

  • Global CSS overriding Shadow DOM (rare)
  • z-index conflicts with your game UI

Solution:

/* If the button is hidden behind your game */
#betahub-widget-container {
  z-index: 10000 !important;
}

Email Field Not Showing/Hiding Correctly

Check Configuration:

// Mode 1: Anonymous (no email for bugs/suggestions)
BetaHubWidget.init({ projectId: '...', authToken: '...' });

// Mode 2: Required (email required for all)
BetaHubWidget.init({ projectId: '...', authToken: '...', requireEmail: true });

// Mode 3: Prefilled visible
BetaHubWidget.init({ projectId: '...', authToken: '...', userEmail: 'user@example.com', showEmailField: 'always' });

// Mode 4: Prefilled hidden
BetaHubWidget.init({ projectId: '...', authToken: '...', userEmail: 'user@example.com', showEmailField: 'never' });

// Mode 5: Prefilled auto (smart default)
BetaHubWidget.init({ projectId: '...', authToken: '...', userEmail: 'user@example.com' });

Remember: Support tickets always require email, regardless of configuration.

Advanced Usage

Dynamic Configuration Updates

You can reinitialize the widget with different settings:

// Initial setup
BetaHubWidget.init({
  projectId: 'your-project-id',
  authToken: 'tkn-your-token'
});

// Later, when user logs in
BetaHubWidget.init({
  projectId: 'your-project-id',
  authToken: 'tkn-your-token',
  userEmail: loggedInUser.email,
  customFields: {
    userId: loggedInUser.id,
    playerLevel: loggedInUser.level
  }
});

Note: Calling init() multiple times may create duplicate widget instances. For single-page applications, initialize the widget once and avoid re-initialization unless necessary. If you need to update custom fields dynamically, use the updateCustomFields() method instead of re-initializing.

Conditional Loading

Only load the widget in specific environments:

// Only in production
if (process.env.NODE_ENV === 'production') {
  BetaHubWidget.init({
    projectId: 'your-project-id',
    authToken: 'tkn-your-token'
  });
}

// Only for beta testers
if (user.isBetaTester) {
  BetaHubWidget.init({
    projectId: 'your-project-id',
    authToken: 'tkn-your-token',
    userEmail: user.email
  });
}

Tracking Submission Success

The widget doesn’t expose submission callbacks, but you can monitor network requests:

// Listen for successful submissions
window.addEventListener('betahub-submission-success', function(e) {
  console.log('Feedback submitted successfully!');
  // Show custom thank you message
});

Note: This event is not yet implemented in the current version but is planned for future releases.

File Structure

betahub-html-widget/
├── betahub-widget.js           # Main widget (single file, zero dependencies)
├── demo.html                   # Live demo with documentation
└── README.md                   # Technical documentation

Next Steps

Getting Help

For issues with the HTML widget:


The HTML Feedback Widget is open source and contributions are welcome! Visit the GitHub repository to contribute.