Embedding GazePlotter
GazePlotter is also a Svelte component library. The whole workspace (canvas,
rail, settings pane, modals) ships as one <GazePlotter> component for use
in your own application: Tauri or Electron shells, web apps, other
eye-tracking software.
The component is self-contained: it injects its own design tokens into the document head and needs no server.
Requirements
- Svelte 5 and a Vite-based build.
worker: { format: 'es' }in your Vite config (parsing runs in a module web worker).- Peer dependencies:
jszip,lucide-svelte.
License
GazePlotter is GPL-3.0. The GPL restricts distribution, not use:
- Pointing a webview, iframe, or browser at a hosted GazePlotter: no obligations on your application.
- Shipping the built app as a separate program (own webview, data exchanged via files, URLs, or messages): aggregation. Keep the license notices and link the source repository; your application keeps its own license.
- Distributing a modified GazePlotter: publish the modifications under GPL-3.0.
- Importing the library into your codebase, as shown on this page: the combined application must be GPL-3.0.
Summary, not legal advice; the license text governs.
Minimal usage
<script>
import { GazePlotter } from 'gazeplotter'
</script>
<GazePlotter load={async () => []} /> load prepares the initial files: an async function returning File[]. [] opens an empty workspace; returned files parse immediately. Bytes from
anywhere can be wrapped:
const load = async () => {
const bytes = await fetchRecordingFromMyApp()
return [new File([bytes], 'recording.csv')]
} A ready-made loader for URLs:
<script>
import { GazePlotter, fromUrl } from 'gazeplotter'
const load = fromUrl('https://example.com/recording.csv')
</script>
<GazePlotter {load} /> The options prop
Everything else is one optional options object (GazePlotterOptions).
Every field is optional; every default is the plain web behavior.
<GazePlotter
{load}
options={{
defaultLayout: [{ type: 'scarf', x: 0, y: 0 }],
saveFile: (content, fileName, extension) => mySave(content, fileName),
openFiles: async () => myFilePicker(),
colors: { brand: '#0055ff' },
}}
/> defaultLayout: GridItemSnapshot[]: layout used when loaded data carries none (fresh parses, empty workspace). Workspace files keep their saved layout; omitted snapshot fields get per-plot defaults.saveFile: (content, fileName, extension) => void: delivers one export file. Default: browser download.fileNamearrives with the extension applied;extensionis separate for native save-dialog filters.openFiles: () => Promise<File[]>: what the upload button opens. Default: browser file picker.[]means cancelled. Drag-and-drop is unaffected.colors: GazePlotterColors: partial palette override; unset keys keep the builtin, borders and shadows followblack. Applies reactively, so themes switch without remounting.
The web defaults are exported for wrapping instead of replacing: triggerDownload, openFilesViaBrowser, INGEST_FILE_ACCEPT.
Instance and session
<script>
let plotter
</script>
<GazePlotter bind:this={plotter} {load} /> plotter.resetLayout(): re-runsload.plotter.getSession(): the session object. Useful entries:ingest.loadFiles(files)(same pipeline as drag-and-drop),ingest.openAndLoadFiles()(what the upload button calls),ingest.applyEmpty()(clear all data).
Desktop shells (Tauri, Electron)
Electron works with the defaults: Chromium handles anchor downloads and the file input opens a native dialog.
In Tauri, anchor downloads do nothing on macOS, so wire saveFile to the
dialog and fs plugins (sketch):
import { save } from '@tauri-apps/plugin-dialog'
import { writeFile, writeTextFile } from '@tauri-apps/plugin-fs'
const saveFile = async (content, fileName, extension) => {
const path = await save({
defaultPath: fileName,
filters: [{ name: extension.slice(1).toUpperCase(), extensions: [extension.slice(1)] }],
})
if (!path) return
if (typeof content === 'string') await writeTextFile(path, content)
else await writeFile(path, new Uint8Array(await content.arrayBuffer()))
} Tauri configuration:
- Window
dragDropEnabled: false, so HTML5 drag-and-drop reaches the webview. - CSP must allow
blob:URLs and workers.
The __APP_VERSION__ build define is optional; without it, the citation
line and workspace metadata report unknown. To set it:
define: { __APP_VERSION__: JSON.stringify('your-app 1.0') }