Skip to content

Headless Engines

Headless engines provide game logic without UI, letting you build custom visuals while reusing battle-tested state machines.

Philosophy

Traditional component libraries couple logic and presentation:

vue
<!-- Coupled - can't change visuals without forking -->
<SlotMachine :reels="3" theme="vegas" />

Fortune Kit separates them:

vue
<script setup>
// Logic is a composable
const { spin, isSpinning, result } = useWheel(options)
</script>

<template>
  <!-- UI is yours -->
  <MyCustomWheel :rotation="rotation" @click="spin" />
</template>

Available Engines

useWheel

Spin-to-win wheel logic:

ts
import { useWheel } from '@fortune-kit/headless'

const { spin, isSpinning, result, segmentAngle } = useWheel({
  segments: [
    { id: '1', label: '100 pts', value: 100, weight: 10 },
    { id: '2', label: '500 pts', value: 500, weight: 3 },
    { id: '3', label: 'JACKPOT', value: 10000, weight: 1 }
  ],
  spinDuration: 4000,
  onSpinEnd: (winner) => console.log('Won:', winner)
})

Features:

  • Weighted random selection
  • Configurable spin duration
  • Rotation calculation for CSS transforms
  • Start/reset controls

useQuiz

Quiz/trivia game logic:

ts
import { useQuiz } from '@fortune-kit/headless'

const { currentQuestion, answer, score, progress, isComplete } = useQuiz({
  questions: [...],
  shuffleQuestions: true,
  onAnswer: (answer) => console.log('Answered:', answer),
  onComplete: (answers, score) => console.log('Final:', score)
})

Features:

  • Question navigation (next/prev/goTo)
  • Answer tracking with timing
  • Score calculation
  • Shuffle support
  • Progress tracking

Building Custom UI

Example: Canvas Wheel

vue
<script setup>
import { useWheel } from '@fortune-kit/headless'
import { onMounted, watch } from 'vue'

const { state, spin, segmentAngle } = useWheel({ segments })

const canvas = ref<HTMLCanvasElement>()

watch(() => state.value.currentRotation, (rotation) => {
  // Redraw wheel at new rotation
  drawWheel(canvas.value, rotation)
})
</script>

<template>
  <canvas ref="canvas" @click="spin" />
</template>

Example: 3D Quiz Cards

vue
<script setup>
import { useQuiz } from '@fortune-kit/headless'

const { currentQuestion, answer } = useQuiz({ questions })
</script>

<template>
  <div class="perspective-1000">
    <div
      v-for="option in currentQuestion.options"
      class="transform-3d hover:rotate-y-10"
      @click="answer(option.id)"
    >
      {{ option.text }}
    </div>
  </div>
</template>

Testing

Headless engines are easy to unit test:

ts
import { useWheel } from '@fortune-kit/headless'

test('wheel selects weighted random segment', async () => {
  const { spin, result } = useWheel({
    segments: [
      { id: 'a', label: 'A', value: 1, weight: 100 },
      { id: 'b', label: 'B', value: 2, weight: 0 }
    ]
  })

  await spin()

  // With weight 0, 'b' should never win
  expect(result.value?.id).toBe('a')
})

Built for iGaming with Vue 3