FunPlayer

July 19, 2025 ยท View on GitHub

Synchronized media and haptic playback for web applications

FunPlayer is a React component and Web Component that provides synchronized video/audio playback with haptic feedback through Intiface Central and compatible devices.

Features

  • ๐ŸŽฌ Media Playback: Video, audio, and funscript-only playback
  • ๐ŸŽฎ Haptic Sync: Precise synchronization with funscript data
  • ๐Ÿ”Œ Device Support: Compatible with all Buttplug.js supported devices
  • ๐ŸŽจ Customizable: Theming support with CSS variables
  • ๐Ÿ“ฑ Cross-Platform: Works on desktop, mobile, and VR browsers
  • โšก Performance: Optimized for smooth playback and responsive haptics

Installation

npm install funplayer

Quick Start

React Component

import { FunPlayer } from 'funplayer'

function App() {
  const playlist = [
    {
      sources: [
        { src: 'video.mp4', type: 'video/mp4' }
      ],
      funscript: { actions: [/* funscript data */] },
      name: 'Example Video'
    }
  ]

  return (
    <FunPlayer 
      playlist={playlist}
      onPlay={() => console.log('Playing')}
      onDeviceConnect={(device) => console.log('Device connected:', device)}
    />
  )
}

Web Component (Embed)

<!-- Include the embed script -->
<script src="https://unpkg.com/funplayer/build-embed/funplayer-embed.js"></script>

<!-- Use the web component -->
<fun-player 
  playlist='[{"sources":[{"src":"video.mp4","type":"video/mp4"}],"funscript":{"actions":[...]}}]'
  theme='{"primaryColor":"#FF4B4B"}'
></fun-player>

API Reference

Props

playlist (Array, optional)

Array of playlist items. Each item follows the Video.js format with funscript integration:

{
  sources: [                    // Media sources (Video.js format)
    {
      src: 'video_720p.mp4',    // URL, local file, or data
      type: 'video/mp4',        // MIME type (auto-detected if omitted)
      label: '720p'             // Quality label (optional)
    }
  ],
  funscript: {                  // Haptic data
    actions: [                  // Array of action points
      { at: 1000, pos: 50 },    // at: timestamp (ms), pos: position (0-100)
      { at: 2000, pos: 80 }
    ]
  },
  name: 'Video Title',          // Display name
  description: 'Description',   // Optional description
  poster: 'poster.jpg',         // Poster image URL (optional)
  duration: 120.5,              // Duration in seconds (optional)
  textTracks: [...]             // Subtitles/captions (optional)
}

theme (Object, optional)

Customize the player appearance:

{
  // Colors
  primaryColor: '#FF4B4B',                    // Primary accent color
  backgroundColor: '#FFFFFF',                 // Main background
  secondaryBackgroundColor: '#F0F2F6',        // Secondary background
  textColor: '#262730',                       // Text color
  borderColor: '#E6EBF5',                     // Border color
  
  // Typography & Layout
  fontFamily: '"Source Sans Pro", sans-serif', // Font family
  baseRadius: '0.5rem',                        // Border radius
  spacing: '1rem',                             // Base spacing unit
  
  // Predefined base
  base: 'dark'                                // 'light' or 'dark'
}

Events

FunPlayer exposes standard Video.js events plus haptic-specific events:

Media Events

EventCallback PropDescription
playonPlayPlayback started
pauseonPausePlayback paused
endedonEndedPlayback finished
timeupdateonTimeUpdateCurrent time changed
seekingonSeekingUser started seeking
seekedonSeekedUser finished seeking
loadstartonLoadStartStarted loading media
loadeddataonLoadedDataMedia data loaded
canplayonCanPlayMedia can start playing
erroronErrorPlayback error occurred

Haptic Events

EventCallback PropDescription
deviceconnectonDeviceConnectHaptic device connected
devicedisconnectonDeviceDisconnectHaptic device disconnected
hapticstartonHapticStartHaptic playback started
hapticstoponHapticStopHaptic playback stopped

Playlist Events

EventCallback PropDescription
playlistitemchangeonPlaylistItemChangePlaylist item changed

Event Handler Example

<FunPlayer
  playlist={playlist}
  onPlay={(event) => {
    console.log('Playing:', event.detail)
  }}
  onDeviceConnect={(event) => {
    const device = event.detail
    console.log('Connected device:', device.name)
  }}
  onTimeUpdate={(event) => {
    const currentTime = event.detail.currentTime
    console.log('Current time:', currentTime)
  }}
/>

Setup Requirements

Browser Requirements

  • Modern browser with WebRTC support
  • HTTPS required for device access (except localhost)
  • WebAssembly support for audio processing

Haptic Setup

  1. Install Intiface Central
  2. Connect your compatible device
  3. Start Intiface Central server (default: ws://localhost:12345)
  4. Allow browser permissions for device access

Supported Devices

FunPlayer works with all Buttplug.js compatible devices, including:

  • Interactive toys (strokers, vibrators, etc.)
  • Game controllers with haptic feedback
  • Custom hardware via Buttplug device drivers

Advanced Usage

Programmatic Control

// Access the player instance (React)
const playerRef = useRef()

// Control playback
playerRef.current?.play()
playerRef.current?.pause()
playerRef.current?.seek(30) // Seek to 30 seconds

// Get player state
const currentTime = playerRef.current?.getCurrentTime()
const isPlaying = playerRef.current?.isPlaying()

Custom Theming

FunPlayer uses CSS custom properties for theming. You can override these directly:

.funplayer-container {
  --fp-primary-color: #your-color;
  --fp-background-color: #your-bg;
  --fp-text-color: #your-text;
}

Web Component Events

For the Web Component version, listen to events on the element:

const player = document.querySelector('fun-player')

player.addEventListener('funplayer-play', (event) => {
  console.log('Player started:', event.detail)
})

player.addEventListener('funplayer-deviceconnect', (event) => {
  console.log('Device connected:', event.detail)
})

Browser Support

  • Chrome/Chromium 90+
  • Firefox 90+
  • Safari 14+
  • Edge 90+

License

MIT