This guide is intended for users with frontend development experience (particularly those comfortable working in JavaScript or React) and is ideal for partner teams with some amount of engineering support.

What you need to take advantage of this feature:

Player JS

Our embeds are built to be compatible with playerjs, so that our partners can interact with our audio in their own frontend applications. The playerjs spec is a widely used, open standard for audio and video interaction on the web.

Embed Customization Options

Example Code

A sample Typescript class is provided below to show a potential implementation of our custom embed integrated with playerjs:

export class HighlightPlayer {
  player: playerjs.Player;
  isPlaying: boolean;
  isInitialized: boolean;
  iframe: HTMLIFrameElement;

  constructor(options: {
    containerId: string;
    highlightId: number;
    highlightOptions?: HighlightOptions;
    onReady?: (player: playerjs.Player) => void;
  }) {
    const { containerId, highlightId, highlightOptions, onReady } = options;
    const rootElement = window.document.getElementById(containerId);
    if (rootElement) {
      this.isPlaying = false;
      this.isInitialized = false;

      // Set values for embed
      let srcBase = "<https://embed.fora.io>";
      const highlightParam = `hid=${highlightId}`;
      const typeParam =
        highlightOptions?.type === "minimal" ? `&type=minimal` : "";
      const fontSizeParam = highlightOptions?.fontSize
        ? `&fsz=${encodeURIComponent(highlightOptions?.fontSize)}`
        : "";
      const fontWeightParam = highlightOptions?.fontWeight
        ? `&fwt=${encodeURIComponent(highlightOptions?.fontWeight)}`
        : "";
      const fontFamilyParam = highlightOptions?.fontFamily
        ? `&ffm=${encodeURIComponent(highlightOptions?.fontFamily)}`
        : "";
      const highlightColorParam = highlightOptions?.highlightColor
        ? `&hic=${encodeURIComponent(highlightOptions?.highlightColor)}`
        : "";
      const textColorParam = highlightOptions?.textColor
        ? `&txc=${encodeURIComponent(highlightOptions?.textColor)}`
        : "";
      const backgroundColorParam = highlightOptions?.backgroundColor
        ? `&bgc=${encodeURIComponent(highlightOptions?.backgroundColor)}`
        : "";
      const scrollingParam = highlightOptions?.scrolling
        ? `&scroll=${encodeURIComponent(highlightOptions?.scrolling)}`
        : "";

      // Create Iframe
      const iframe = document.createElement("iframe");
      iframe.src = `${srcBase}/?${highlightParam}${typeParam}${fontSizeParam}${fontWeightParam}${fontFamilyParam}${highlightColorParam}${backgroundColorParam}${textColorParam}${scrollingParam}`;

      // Default width and height to size of card, or adjust if minimal type is asked for
      iframe.width = "570px";
      iframe.height = "220px";
      if (highlightOptions?.type === "minimal") {
        iframe.width = highlightOptions?.width ?? "100%";
        iframe.height = highlightOptions?.height ?? "100%";
      }

      // Enforce no scrolling and no border on the highlight
      iframe.setAttribute("scrolling", "no");
      iframe.setAttribute("frameBoarder", "0");
      iframe.style.border = "none";
      iframe.style.overflow = "hidden";

      // Inject iframe into DOM
      iframe.allow = "autoplay";
      iframe.onload = (e) => this.onLoad(e, onReady);
      this.iframe = iframe;
      rootElement.replaceChildren(iframe);
    } else {
      console.error(`The element with id, ${containerId}, was not fond`);
    }
  }

  // Setting up playerJS functionality. The spect can be found here
  // <https://github.com/embedly/player.js/blob/master/src/player.js>

  onLoad(
    e,
    onReady: (player: playerjs.Player) => void = () => {
      return;
    }
  ) {
    try {
      const player = new playerjs.Player(e.target);
      player.on("ready", () => {
        onReady(player);
        this.isInitialized = true;
      });
      this.player = player;
    } catch (err) {
      console.error(err);
      console.error("This iframe is not compatible with playerjs");
    }
  }

  play() {
    this.player.play();
    this.isPlaying = true;
  }
  pause() {
    this.player.pause();
    this.isPlaying = false;
  }
  getPaused(callback: (value: boolean) => void) {
    this.player.getPaused(callback);
  }
  mute() {
    this.player.mute();
  }
  unmute() {
    this.player.unmute();
  }
  getMuted(callback: (value: boolean) => void) {
    this.player.getMuted(callback);
  }
  setVolume(value: number) {
    this.player.setVolume(value);
  }
  getVolume(callback: (value: number) => void) {
    this.player.getVolume(callback);
  }
  getDuration(callback: (value: number) => void) {
    this.player.getDuration(callback);
  }
  setCurrentTime(value: number) {
    this.player.setCurrentTime(value);
  }
  getCurrentTime(callback: (value: number) => void) {
    this.player.getCurrentTime(callback);
  }
  setLoop(value: boolean) {
    this.player.setLoop(value);
  }
  getLoop(callback: (value: boolean) => void) {
    this.player.getLoop(callback);
  }
  removeEventListener(event: playerjsEVENTS, callback: () => void) {
    this.player.removeEventListener(event, callback);
  }
  addEventListener(event: playerjsEVENTS, callback: () => void) {
    this.player.addEventListener(event, callback);
  }
  on(event: playerjsEVENTS, callback: () => void) {
    this.player.on(event, callback);
  }
  off(event: playerjsEVENTS, callback: () => void) {
    this.player.off(event, callback);
  }
  getIsPlaying() {
    return this.isPlaying;
  }
}

These are the types for the class above:

export interface HighlightOptions {
  width?: string;
  height?: string;
  type?: "minimal" | "lvn";
  fontWeight?: string;
  fontSize?: string;
  fontFamily?: FontFamlily;
  italics?: boolean;
  highlightColor?: string;
  textColor?: string;
  backgroundColor?: string;
  scrolling?: boolean;
  secret?: string;
}

type FontFamlily =
  | "Lato"
  | "Merriweather"
  | "Montserrat"
  | "Open Sans"
  | "Oswald"
  | "Roboto"
  | "Signika";

export type playerjsEVENTS =
  | "ready"
  | "play"
  | "pause"
  | "ended"
  | "timeupdate"
  | "progress"
  | "error";

Getting Metadata From Window Message:

<script>
  window.addEventListener("message", (event) => {
    if (event.origin !== "<https://embed.cortico.ai>") {
      return;
    }
    const highlightId = event.data.highlight_id;
		const highlightMetadata = event.data.data;
  });
</script>