GALABET FAIR / OPEN-SOURCE GAME VERIFICATION

Build the game.
Let anyone verify.

Give every result a verifiable record.
An open library for seed commitments, game outcomes, and independent checks.

GFS/1.0 · Zero runtime dependencies · Browser & server

01 / COMMIT

Before the round.

Server seed commitment
ab37723062965715c5e6eeb5…
Client seed
galabet
Nonce
42

Publish the commitment first.
Keep the server seed secret until reveal.

02 / REPRODUCE

After the reveal.

56.12

Recompute the roll from the revealed inputs.
Check that the seed matches its commitment.

Check this example ↗

A public Dice example, calculated with the reference library.

START WITH THE LIBRARY

Your application.
Verifiable outcomes.

Commit to a seed, derive a result, then make the completed round independently checkable. Use the same core library across your stack.

round.mjs
SOURCE / JAVASCRIPT
import { commit, play } from '@galabet/fair';

const seeds = {
  serverSeed: '5c1f7d3e8a2b4c6d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d',
  clientSeed: 'galabet',
  nonce: 42,
};
const { commitment } = await commit(seeds.serverSeed);
const { result, cursor } = await play({ game: 'dice', ...seeds });
console.log({ commitment, result, cursor });
EXECUTION PREVIEW
$ npm install @galabet/fair
✓ core library ready

Example output: result 56.12, cursor 0. Verification matches.

Replaying example

Run the reference library directly in Node.js. Public example seeds; output computed with the library. Server execution is illustrated.

A magnifying glass inspecting matching rows on a sealed recordGalabet’s lunar navigator comparing a seed capsule with its result recordINSPECT. RECOMPUTE. COMPARE.
WHAT VERIFICATION ESTABLISHES

Check the result.
Know the limits.

A matching commitment confirms that the revealed server seed is the one committed to earlier. Recomputing the outcome checks that the published inputs produce the recorded result.

This verifies the calculation. It does not establish favourable odds, guarantee withdrawals, or certify an operator’s business practices.

Inspect a completed round ↗
THE GAME COLLECTION

Different games.
Every result explained.

Explore what each mapper produces. These examples run in your browser with public seeds. Change the nonce, inspect the output, then reproduce it in the verifier.

01 / THE GAME COLLECTION1 float

Dice.

A roll you can reproduce.

A single number from the seed sequence becomes a roll between 0.00 and 100.00. Reveal the seed and reproduce the exact result.

56.12
0.00255075100.00
SEED → FLOAT → ROLL
nonce 42Verify result ↗
Mapping & code

Dice consumes one float. The mapper applies floor(f × 10001) / 100. The over/under target and payout belong to the game rules, separate from outcome generation.

const { result } = await play({ game: 'dice', ...seeds });

Import play from @galabet/fair. Use the public example seeds with nonce 42.

Play Dice and read the full explanation ↗
02 / THE GAME COLLECTION1 float

Limbo.

One number. A multiplier.

Convert one seed-derived number into a multiplier. The declared house edge is part of the calculation, so it can be reproduced too.

1.76×DECLARED EDGE 1%
nonce 42Verify result ↗
Mapping & code

The mapper applies floor((1e8 / (f × 1e8 + 1)) × (1 − houseEdge) × 100) / 100, with a minimum of 1.00. Lower input floats produce higher multipliers.

const { result } = await play({
  game: 'limbo', ...seeds, params: { houseEdge: 0.01 }
});

Import play from @galabet/fair. Use the public example seeds with nonce 42.

03 / THE GAME COLLECTION16 floats

Plinko.

Trace every turn.

Sixteen rows. One seed-derived decision at each peg. Replay the complete path, from the first turn to the final bucket.

↘↙↙↘↙↘↘↘↙↙↘↘↘↙↙↙
16 TURNS / BUCKET 8
nonce 42Verify result ↗
Mapping & code

Each float below 0.5 sends the ball left; all others send it right. The bucket is the count of right turns. Payout tables are separate from the path mapper.

const { result } = await play({
  game: 'plinko', ...seeds, params: { rows: 16 }
});
// result: { path, bucket }

Import play from @galabet/fair. Use the public example seeds with nonce 42.

04 / THE GAME COLLECTION24 floats

Mines.

The board is already set.

Shuffle all 25 positions, then select the mines. A completed round can be checked against that original layout.

000102030405060708×10111213141516×18192021×2324
nonce 42Verify result ↗
Mapping & code

Fisher–Yates consumes 24 floats, one per swap. The first three shuffled positions become mines. This example reveals the board; a live game must keep it hidden until the round ends.

const { result } = await play({
  game: 'mines', ...seeds, params: { mines: 3 }
});
// result: sorted mine positions, indexed from 0

Import play from @galabet/fair. Use the public example seeds with nonce 42.

05 / THE GAME COLLECTION1 float

Roulette.

Thirty-seven possible stops.

Map one float to a European roulette pocket, including zero. The outcome is reproducible independently of the bet placed.

20EUROPEAN / 0–36
nonce 42Verify result ↗
Mapping & code

floor(f × 37) returns a pocket from 0 to 36. Colour, parity, straight bets, and payouts are game rules applied after this result.

const { result } = await play({ game: 'roulette', ...seeds });

Import play from @galabet/fair. Use the public example seeds with nonce 42.

06 / MULTIPLAYER PROFILE

Crash.

One round. A shared result.

Crash uses a hash chain instead of individual player seeds. Each revealed hash links to the previous one; a published salt determines the multiplier.

An explanatory replay with public inputs. This is not a live round.

PREVIOUS HASH ab3772306296…
GAME HASH ••••••••••••
?.??×
REVEAL THE HASH TO REPRODUCE THE MULTIPLIER
Inspect the crash calculation

The first 52 HMAC bits determine the multiplier. A live chain must be committed before its external salt becomes known. This fixed-salt example only demonstrates the calculation.

import { crashResult, verifyCrashLink } from '@galabet/fair';

const gameHash = '5c1f7d3e8a2b4c6d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d';
const previousHash = 'ab37723062965715c5e6eeb54816f909a8e787bd3bfaee67a3cc0a3b90707de7';
const linkMatches = await verifyCrashLink(gameHash, previousHash);
const multiplier = await crashResult(gameHash, 'galabet-homepage-demo', 0.01);
// linkMatches: true · multiplier: 6.23
THE DEALING TABLE

Every card. Every draw.
In a reproducible order.

Card games share a shuffle. Keno draws from a numbered pool. Wheel selects a segment. Each starts with the same seed derivation and follows its own mapping rules.

07 / THE GAME COLLECTION51 floats

Blackjack.

Verify the whole deck.

Start with a reproducible shuffle. Every card has a fixed place in the dealing order, ready for your blackjack rules.

7♣♣7K♣♣K3♠♠3A♣♣AQ♥♥Q
nonce 42Verify result ↗
Mapping & code

One deck consumes 51 floats across seven HMAC digests. Up to eight decks are supported. The library returns the deck order; scoring, player actions, and settlement are implemented by the game.

const { result } = await play({
  game: 'blackjack', ...seeds, params: { decks: 1 }
});
// result: 52 card labels in dealing order

Import play from @galabet/fair. Use the public example seeds with nonce 42.

08 / THE GAME COLLECTION51 floats

Hi–Lo.

The next card has its place.

Reveal cards in sequence from a shuffled deck. Each step can be reconstructed after the seed is disclosed.

7♣♣7K♣♣K
nonce 42Verify result ↗
Mapping & code

Hilo uses the same deck mapper as Blackjack. A card label combines rank and suit: AC is ace of clubs, TD is ten of diamonds. Higher/lower decisions are handled by the game.

const { result } = await play({ game: 'hilo', ...seeds });
const [currentCard, nextCard] = result;

Import play from @galabet/fair. Use the public example seeds with nonce 42.

09 / THE GAME COLLECTION39 floats

Keno.

Follow the draw.

Shuffle forty numbers and take the first ten. The same inputs always reproduce the same set, without repeated numbers.

12345678910111213141516171819202122232425262728293031323334353637383940
nonce 42Verify result ↗
Mapping & code

Keno consumes 39 floats to shuffle numbers 1–40. The selected numbers are returned in ascending order. Player picks and hit-based payouts are separate.

const { result } = await play({
  game: 'keno', ...seeds, params: { draws: 10 }
});

Import play from @galabet/fair. Use the public example seeds with nonce 42.

10 / THE GAME COLLECTION1 float

Wheel.

A result for every segment.

Choose the number of segments. One float selects the outcome; your game defines the value of each segment.

SEGMENT5
10 SEGMENTS / ZERO-BASED INDEX
nonce 42Verify result ↗
Mapping & code

floor(f × segments) returns a zero-based segment index. This example uses ten equal segments; the mapper does not assign their payout values.

const { result } = await play({
  game: 'wheel', ...seeds, params: { segments: 10 }
});

Import play from @galabet/fair. Use the public example seeds with nonce 42.

Public inputs used in these examples
const seeds = {
  serverSeed: '5c1f7d3e8a2b4c6d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d',
  clientSeed: 'galabet',
  nonce: 42, // incremented by each “next example” button
};

These are demonstration seeds, visible to everyone. Live games keep server seeds secret until reveal.

BUILD YOUR OWN IMPLEMENTATION

Different code.
Identical results.

Compare your implementation against published test vectors. Check the seed commitment, byte derivation, game mapping, and crash profile against known inputs and expected outputs.

Explore the test vectors ↗
2,280Core & game vectors
48Crash vectors
2Signing vectors
$ pnpm vectors:checkRun from the project repository.
FROM THE FIRST COMMITMENT TO THE FINAL CHECK

Make the result
independently checkable.

Explore an integration or follow a complete Dice round.