saltfish
v0.3.82
Published
An interactive video-guided tour system for web applications
Maintainers
Readme
Saltfish
Interactive video-guided tours for web applications. Create engaging onboarding experiences with synchronized video playback, interactive overlays, and smart triggering.
Features
- 🎥 Video-Guided Tours - Synchronized video content with interactive UI overlays
- 🎯 Smart Triggers - Automatic playlist triggering based on URL, user behavior, and custom conditions
- 📱 Responsive - Automatically adapts to different screen sizes and devices
- 🎨 Shadow DOM - Complete style isolation prevents CSS conflicts
- 📊 Analytics - Built-in tracking for user engagement and completion rates
- 🔄 State Persistence - Resume tours where users left off
- 🌐 Multi-language - User-specific language preferences
- ⚡ Lightweight - Minimal bundle size with efficient loading
Installation
CDN (Recommended)
<script src="https://storage.saltfish.ai/player/player.js"></script>NPM
npm install saltfishQuick Start
Basic Setup (CDN)
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
</head>
<body>
<h1>Welcome to My App</h1>
<!-- Load Saltfish -->
<script src="https://storage.saltfish.ai/player/player.js"></script>
<script>
// 1. Initialize with your token
saltfish.init({
token: 'YOUR_TOKEN_HERE',
enableAnalytics: true
});
// 2. Identify the user
saltfish.identify('user-123', {
email: '[email protected]',
name: 'John Doe',
language: 'en'
});
// 3. (Optional) Manually start a playlist
saltfish.startPlaylist('playlist-id');
</script>
</body>
</html>NPM/ES Modules
import saltfish from 'saltfish';
// Initialize
saltfish.init({
token: 'YOUR_TOKEN_HERE',
enableAnalytics: true
});
// Identify user
saltfish.identify('user-123', {
email: '[email protected]',
language: 'en'
});
// Start playlist
saltfish.startPlaylist('playlist-id');API Reference
saltfish.init(config)
Initialize the Saltfish player. Must be called before any other methods.
Parameters:
config(Object or String):- If string: Your API token
- If object:
token(String, required): Your API token from Saltfish dashboardenableAnalytics(Boolean, optional): Enable/disable analytics tracking (default:true)
Returns: Promise<void> (can be called without await)
Examples:
// Simple initialization with token only
saltfish.init('YOUR_TOKEN_HERE');
// With configuration object
saltfish.init({
token: 'YOUR_TOKEN_HERE',
enableAnalytics: false // Disable analytics for development
});
// Using async/await
await saltfish.init('YOUR_TOKEN_HERE');
// Using events
saltfish.on('initialized', () => {
console.log('Saltfish ready!');
});
saltfish.init('YOUR_TOKEN_HERE');saltfish.identify(userId, attributes)
Identify the current user. This enables playlist triggers, progress tracking, and personalized experiences.
Parameters:
userId(String, required): Unique identifier for the userattributes(Object, optional):email(String): User's email addressname(String): User's display namelanguage(String): Language preference ('en','sv','es','fr','de', or'auto'for browser detection)- Any custom attributes you want to track
Examples:
// Basic identification
saltfish.identify('user-123');
// With email and name
saltfish.identify('user-123', {
email: '[email protected]',
name: 'John Doe'
});
// With language preference
saltfish.identify('user-123', {
email: '[email protected]',
language: 'sv' // Swedish
});
// Auto-detect language from browser
saltfish.identify('user-123', {
language: 'auto'
});
// With custom attributes
saltfish.identify('user-123', {
email: '[email protected]',
plan: 'premium',
signupDate: '2024-01-15'
});saltfish.identifyAnonymous(attributes)
Identify anonymous users without backend communication. Uses localStorage for progress tracking and trigger evaluation.
Parameters:
attributes(Object, optional): Same asidentify()method
Examples:
// Anonymous user with language preference
saltfish.identifyAnonymous({
language: 'en'
});
// Anonymous user with no data
saltfish.identifyAnonymous();
// With custom tracking data
saltfish.identifyAnonymous({
language: 'auto',
theme: 'dark'
});saltfish.startPlaylist(playlistId, options)
Manually start a specific playlist. If a playlist is already running, it will be reset and the new one started.
Parameters:
playlistId(String, required): The playlist ID from your Saltfish dashboardoptions(Object, optional):startNodeId(String): Start from a specific step instead of the beginningposition(String): Player position ('bottom-left'or'bottom-right')
Returns: Promise<void> (can be called without await)
Examples:
// Basic usage
saltfish.startPlaylist('playlist-123');
// Start from a specific step
saltfish.startPlaylist('playlist-123', {
startNodeId: 'step-5'
});
// With custom position
saltfish.startPlaylist('playlist-123', {
position: 'bottom-right'
});
// Using async/await
await saltfish.startPlaylist('playlist-123');
// Using events
saltfish.on('playlistStarted', (data) => {
console.log('Started:', data.playlist.id);
});
saltfish.startPlaylist('playlist-123');Event Listeners
Listen to player events using on() and remove listeners with off().
saltfish.on(eventName, handler)
Register an event listener.
Available Events:
'initialized'- Player initialized successfully'playlistStarted'- Playlist has started playing'playlistEnded'- Playlist completed'playlistDismissed'- User dismissed/closed the playlist'stepStarted'- New step/video started'stepEnded'- Step/video completed'error'- An error occurred
Examples:
// Listen for playlist completion
saltfish.on('playlistEnded', (data) => {
console.log('Playlist completed:', data.playlist.id);
console.log('Completion rate:', data.completionRate);
});
// Listen for step changes
saltfish.on('stepStarted', (data) => {
console.log('Now on step:', data.step.id);
});
// Listen for errors
saltfish.on('error', (data) => {
console.error('Player error:', data.message);
});
// Listen for initialization
saltfish.on('initialized', () => {
console.log('Player ready!');
});saltfish.off(eventName, handler)
Remove an event listener.
Returns: boolean - true if listener was removed, false if not found
Example:
function onPlaylistEnd(data) {
console.log('Playlist ended:', data.playlist.id);
}
// Add listener
saltfish.on('playlistEnded', onPlaylistEnd);
// Remove listener
saltfish.off('playlistEnded', onPlaylistEnd);Other Methods
saltfish.getSessionId()
Get the current session ID.
Returns: String
const sessionId = saltfish.getSessionId();
console.log('Session ID:', sessionId);saltfish.getRunId()
Get the current run ID (unique per playlist execution).
Returns: String | null
const runId = saltfish.getRunId();
console.log('Run ID:', runId);saltfish.resetPlaylist()
Reset the current playlist to its initial state.
saltfish.resetPlaylist();saltfish.destroy()
Destroy the player instance and clean up all resources.
saltfish.destroy();saltfish.version()
Get the current Saltfish player version.
Returns: String
const version = saltfish.version();
console.log('Saltfish version:', version);Playlist Triggers
Playlists can be automatically triggered based on conditions you configure in the Saltfish CMS. No code changes needed!
Trigger Types
URL-Based Triggers
Automatically show playlists when users visit specific URLs:
// Just initialize and identify - triggers happen automatically
saltfish.init('YOUR_TOKEN');
saltfish.identify('user-123');
// Playlist will auto-trigger when user navigates to configured URLs
// e.g., "/dashboard", "/pricing", "/features/*"CMS Configuration Examples:
- Exact match:
/dashboard- Only triggers on exactly/dashboard - Contains:
/dashboardwith "contains" mode - Triggers on/dashboard,/dashboard/settings, etc. - Wildcard:
/products/*- Triggers on any product page - Regex:
/playlist-[0-9]+- Triggers on/playlist-1,/playlist-42, etc.
Element Click Triggers
Trigger playlists when users click specific elements:
<button id="help-button">Help</button>
<button class="support-btn">Support</button>
<button data-action="guide">Guide</button>
<script>
saltfish.init('YOUR_TOKEN');
saltfish.identify('user-123');
// Configure in CMS with CSS selector:
// #help-button, .support-btn, [data-action="guide"]
</script>Conditional Triggers
Configure complex trigger logic in the CMS:
- Once per user: Show playlist only once, never again
- Seen/Not seen: Trigger based on whether user has seen other playlists
- A/B Testing: Show different playlists to different user segments
- Combine conditions: URL + click + seen/not seen with AND/OR logic
Manual Override
You can always manually start a playlist even if triggers are configured:
// Manually start any playlist
saltfish.startPlaylist('onboarding-tour');Common Use Cases
Onboarding Flow
// Initialize on page load
saltfish.init({
token: 'YOUR_TOKEN',
enableAnalytics: true
});
// Identify user after signup
saltfish.identify(userId, {
email: userEmail,
language: 'auto',
plan: 'free'
});
// Backend triggers will automatically show onboarding
// when configured in CMS for new usersFeature Announcement
saltfish.init('YOUR_TOKEN');
saltfish.identify(userId);
// Configure in CMS to trigger on /new-feature page
// Playlist auto-plays when user visitsHelp Button
<button id="help-btn">Help</button>
<script>
saltfish.init('YOUR_TOKEN');
saltfish.identify(userId);
// Configure in CMS with element selector: #help-btn
// Playlist auto-triggers when user clicks
</script>Context-Specific Tours
// Different tours for different pages
saltfish.init('YOUR_TOKEN');
saltfish.identify(userId);
// Configure in CMS:
// - "/dashboard" → dashboard-tour
// - "/settings" → settings-tour
// - "/billing" → billing-tour
// Tours trigger automatically based on URLFramework Integration
React
import { useEffect } from 'react';
import saltfish from 'saltfish';
function App() {
useEffect(() => {
// Initialize Saltfish
saltfish.init({
token: process.env.REACT_APP_SALTFISH_TOKEN,
enableAnalytics: true
});
// Identify user when auth state changes
if (user) {
saltfish.identify(user.id, {
email: user.email,
name: user.name,
language: 'auto'
});
}
// Cleanup on unmount
return () => {
saltfish.destroy();
};
}, [user]);
return <div>Your App</div>;
}Vue
<template>
<div>Your App</div>
</template>
<script>
import saltfish from 'saltfish';
export default {
mounted() {
saltfish.init({
token: process.env.VUE_APP_SALTFISH_TOKEN,
enableAnalytics: true
});
if (this.user) {
saltfish.identify(this.user.id, {
email: this.user.email,
language: 'auto'
});
}
},
beforeUnmount() {
saltfish.destroy();
}
};
</script>Angular
import { Component, OnInit, OnDestroy } from '@angular/core';
import saltfish from 'saltfish';
@Component({
selector: 'app-root',
template: '<div>Your App</div>'
})
export class AppComponent implements OnInit, OnDestroy {
ngOnInit() {
saltfish.init({
token: environment.saltfishToken,
enableAnalytics: true
});
if (this.authService.user) {
saltfish.identify(this.authService.user.id, {
email: this.authService.user.email,
language: 'auto'
});
}
}
ngOnDestroy() {
saltfish.destroy();
}
}Next.js
// app/layout.js or pages/_app.js
'use client';
import { useEffect } from 'react';
import saltfish from 'saltfish';
export default function RootLayout({ children }) {
useEffect(() => {
if (typeof window !== 'undefined') {
saltfish.init({
token: process.env.NEXT_PUBLIC_SALTFISH_TOKEN,
enableAnalytics: true
});
// Identify user if logged in
// saltfish.identify(userId, { email, language: 'auto' });
}
return () => {
if (typeof window !== 'undefined') {
saltfish.destroy();
}
};
}, []);
return <html><body>{children}</body></html>;
}TypeScript Support
Full TypeScript definitions included:
import saltfish, { SaltfishAPI } from 'saltfish';
// All methods are fully typed
saltfish.init({
token: 'YOUR_TOKEN',
enableAnalytics: true
});
saltfish.identify('user-123', {
email: '[email protected]',
language: 'en'
});
saltfish.startPlaylist('playlist-id', {
startNodeId: 'step-2'
});
// Event handlers are typed
saltfish.on('playlistEnded', (data) => {
console.log(data.playlist.id); // Fully typed
console.log(data.completionRate);
});Browser Support
- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
- Opera 76+
Getting Started
- Sign up at saltfish.ai
- Get your token from the dashboard
- Create playlists using the CMS
- Configure triggers (optional) for automatic playback
- Install Saltfish in your app
Support
- 📧 Email: [email protected]
- 🐛 Issues: GitHub Issues
- 📚 Documentation: docs.saltfish.ai
License
PROPRIETARY - See LICENSE file for details.
Made with ❤️ by Saltfish AB
