
Product
Socket for Jira Is Now Available
Socket for Jira lets teams turn alerts into Jira tickets with manual creation, automated ticketing rules, and two-way sync.
Interactive video-guided tours for web applications. Create engaging onboarding experiences with synchronized video playback, interactive overlays, and smart triggering.
<script src="https://storage.saltfish.ai/player/player.js"></script>
npm install saltfish
<!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: 'user@example.com',
name: 'John Doe',
language: 'en'
});
// 3. (Optional) Manually start a playlist
saltfish.startPlaylist('playlist-id');
</script>
</body>
</html>
import saltfish from 'saltfish';
// Initialize
saltfish.init({
token: 'YOUR_TOKEN_HERE',
enableAnalytics: true
});
// Identify user
saltfish.identify('user-123', {
email: 'user@example.com',
language: 'en'
});
// Start playlist
saltfish.startPlaylist('playlist-id');
saltfish.init(config)Initialize the Saltfish player. Must be called before any other methods.
Parameters:
config (Object or String):
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)Examples:
// Basic identification
saltfish.identify('user-123');
// With email and name
saltfish.identify('user-123', {
email: 'user@example.com',
name: 'John Doe'
});
// With language preference
saltfish.identify('user-123', {
email: 'user@example.com',
language: 'sv' // Swedish
});
// Auto-detect language from browser
saltfish.identify('user-123', {
language: 'auto'
});
// With custom attributes
saltfish.identify('user-123', {
email: 'user@example.com',
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 as identify() methodExamples:
// 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');
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 occurredExamples:
// 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);
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);
Playlists can be automatically triggered based on conditions you configure in the Saltfish CMS. No code changes needed!
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:
/dashboard - Only triggers on exactly /dashboard/dashboard with "contains" mode - Triggers on /dashboard, /dashboard/settings, etc./products/* - Triggers on any product page/playlist-[0-9]+ - Triggers on /playlist-1, /playlist-42, etc.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>
Configure complex trigger logic in the CMS:
You can always manually start a playlist even if triggers are configured:
// Manually start any playlist
saltfish.startPlaylist('onboarding-tour');
// 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 users
saltfish.init('YOUR_TOKEN');
saltfish.identify(userId);
// Configure in CMS to trigger on /new-feature page
// Playlist auto-plays when user visits
<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>
// 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 URL
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>;
}
<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>
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();
}
}
// 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>;
}
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: 'user@example.com',
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);
});
PROPRIETARY - See LICENSE file for details.
Made with ❤️ by Saltfish AB
FAQs
An interactive video-guided tour system for web applications
The npm package saltfish receives a total of 384 weekly downloads. As such, saltfish popularity was classified as not popular.
We found that saltfish demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Product
Socket for Jira lets teams turn alerts into Jira tickets with manual creation, automated ticketing rules, and two-way sync.

Company News
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.

Security News
NIST will stop enriching most CVEs under a new risk-based model, narrowing the NVD's scope as vulnerability submissions continue to surge.