Tutorial: Storefront Events for Analytics and Custom Integrations

The Autopictura app embed dispatches custom events on the window object while a customer creates a design on your product page. You can listen to these events from your theme or a custom pixel to track the design journey in your analytics, show your own loading states, or sync selections with an external system. No app changes are needed; the events are dispatched by default.

Where to Add Your Code

Add your listeners somewhere that runs on the product page before or shortly after the Autopictura app embed loads, for example:

  • In your theme's layout/theme.liquid, inside a <script> tag in the <head>
  • In a Custom Liquid section or block on the product template
  • In a Shopify custom pixel (Settings → Customer events), for analytics that must keep working across theme changes

Listeners registered before the embed loads catch every event, including autopicturaReady. Listeners added later still receive all subsequent events.

Stability

Event names and the documented payload properties are a public contract. Properties may be added, but existing ones will not be renamed or removed without a deprecation period, in which case the old form keeps working alongside the new one (see Legacy Event Names below). Only rely on the properties listed here; anything else in event.detail is internal and may change.

Quick Start

// Listen to the ready event
window.addEventListener('autopicturaReady', (event) => {
  console.log('Autopictura is ready!', event.detail.sdk);
});

// Listen to design selection
window.addEventListener('autopicturaDesignSelected', (event) => {
  console.log('Design selected:', event.detail);
});

Available Events

autopicturaReady

Fired once the Autopictura app embed has initialized on the product page and is ready for use.

Detail:

PropertyTypeDescription
sdkobjectThe initialized Autopictura instance

Example:

window.addEventListener('autopicturaReady', (event) => {
  const { sdk } = event.detail;
  // SDK is now available for programmatic access
  console.log('Autopictura initialized');
});

autopicturaStateChanged

Fired whenever the overall generation state changes (selection, deselection, initialization). isInitialized tells you whether the embed has finished setting up, so you can tell "no design yet because it is still loading" apart from "no design selected".

Detail:

PropertyTypeDescription
generationSelectedbooleanWhether a generation is currently selected
generationIdstring | nullThe selected generation's ID
imageUrlstring | nullURL of the selected generated image
designLinkstring | undefinedLink to view/edit the design
isInitializedbooleanWhether the state has been initialized

Example:

window.addEventListener('autopicturaStateChanged', (event) => {
  const { generationSelected, generationId, imageUrl } = event.detail;
  if (generationSelected) {
    console.log('Generation selected:', generationId);
  }
});

autopicturaVariantChanged

Fired when the selected product variant changes.

Detail:

PropertyTypeDescription
variantIdstring | nullThe newly selected variant ID, or null when no variant could be detected

Example:

window.addEventListener('autopicturaVariantChanged', (event) => {
  console.log('Variant changed to:', event.detail.variantId);
});

autopicturaStyleSelected

Fired when a user clicks on a style card to select it.

Detail:

PropertyTypeDescription
styleIdstringThe style's unique ID
styleNamestringThe style's display name

Example:

window.addEventListener('autopicturaStyleSelected', (event) => {
  const { styleName, styleId } = event.detail;
  console.log('Style selected:', styleName);

  // Track in analytics
  gtag('event', 'style_selected', {
    style_id: styleId,
    style_name: styleName
  });
});

autopicturaUploadStarted

Fired when a user selects a file to upload (before cropping). The file name is deliberately not included, because customer-chosen file names can contain personal information and window events are visible to every script on the page.

Detail:

PropertyTypeDescription
fileSizenumberSize of the selected file in bytes

Example:

window.addEventListener('autopicturaUploadStarted', (event) => {
  console.log('Upload started:', `${Math.round(event.detail.fileSize / 1024)}KB`);
});

autopicturaUploadComplete

Fired when the cropped image has been uploaded, or when cropping or uploading failed. If the customer closes the crop dialog without confirming, no upload is attempted and this event does not fire.

Detail:

PropertyTypeDescription
imageIdstring | nullThe uploaded image's unique ID (null if failed)
successbooleanWhether the upload succeeded

Example:

window.addEventListener('autopicturaUploadComplete', (event) => {
  const { success, imageId } = event.detail;
  if (success) {
    console.log('Upload complete, image ID:', imageId);
  } else {
    console.log('Upload failed');
  }
});

autopicturaGenerationStarted

Fired when AI generation begins (after the user clicks "Generate").

Detail:

PropertyTypeDescription
styleIdstringThe style being used for generation
styleNamestringThe style's display name
variantIdstring | nullThe selected variant ID, or null for catch-all products without a detected variant

Example:

window.addEventListener('autopicturaGenerationStarted', (event) => {
  const { styleName, variantId } = event.detail;
  console.log('Generating with style:', styleName);

  // Show loading indicator
  document.body.classList.add('autopictura-generating');
});

autopicturaGenerationComplete

Fired when AI generation completes, whether it succeeded, returned no images, or failed with an error. Every autopicturaGenerationStarted is followed by exactly one autopicturaGenerationComplete.

Detail:

PropertyTypeDescription
generationIdstring | nullThe generation ID (null if failed)
imageUrlstring | nullURL of the generated image (null if failed)
successbooleanWhether the generation was successful

Example:

window.addEventListener('autopicturaGenerationComplete', (event) => {
  const { success, generationId, imageUrl } = event.detail;

  if (success) {
    console.log('Generation complete:', generationId);
    // Track successful generation
    gtag('event', 'generation_complete', { generation_id: generationId });
  } else {
    console.log('Generation failed');
  }

  // Hide loading indicator
  document.body.classList.remove('autopictura-generating');
});

autopicturaDesignSelected

Fired when a user clicks on a generated image to select it for their cart.

Detail:

PropertyTypeDescription
generationIdstringThe selected generation's ID
imageUrlstringURL of the selected image
designLinkstringLink to view/edit the design

Example:

window.addEventListener('autopicturaDesignSelected', (event) => {
  const { generationId, imageUrl } = event.detail;
  console.log('Design selected for cart:', generationId);

  // Update custom UI elements
  updatePreviewImage(imageUrl);
});

autopicturaDesignDeselected

Fired when the selected design is cleared: the customer clicks "Change", switches to another variant, or deletes the selected design.

Detail: Empty object {}

Example:

window.addEventListener('autopicturaDesignDeselected', () => {
  console.log('Design deselected');

  // Reset custom UI elements
  clearPreviewImage();
});

Legacy Event Names

Before the autopictura prefix, three of these events shipped under older names. They are still dispatched with the same payload, so existing theme code keeps working, but new integrations should use the autopictura* names.

Current nameLegacy name
autopicturaReadyaiGeneratorReady
autopicturaStateChangedaiGenerationStateChanged
autopicturaVariantChangedaiVariantIdChanged

Common Use Cases

Analytics Tracking

// Track the full user journey
window.addEventListener('autopicturaStyleSelected', (e) => {
  gtag('event', 'autopictura_style_selected', {
    style_id: e.detail.styleId,
    style_name: e.detail.styleName
  });
});

window.addEventListener('autopicturaGenerationStarted', (e) => {
  gtag('event', 'autopictura_generation_started', e.detail);
});

window.addEventListener('autopicturaGenerationComplete', (e) => {
  gtag('event', 'autopictura_generation_complete', {
    success: e.detail.success,
    generation_id: e.detail.generationId
  });
});

window.addEventListener('autopicturaDesignSelected', (e) => {
  gtag('event', 'autopictura_design_selected', e.detail);
});

Custom Loading States

window.addEventListener('autopicturaGenerationStarted', () => {
  document.querySelector('.custom-loader').style.display = 'block';
});

window.addEventListener('autopicturaGenerationComplete', () => {
  document.querySelector('.custom-loader').style.display = 'none';
});

Sync with External Systems

window.addEventListener('autopicturaDesignSelected', async (event) => {
  // Notify external system
  await fetch('/api/design-selected', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(event.detail)
  });
});

Listening to All Events

For debugging or comprehensive tracking:

const events = [
  'Ready', 'StateChanged', 'VariantChanged', 'StyleSelected',
  'UploadStarted', 'UploadComplete', 'GenerationStarted',
  'GenerationComplete', 'DesignSelected', 'DesignDeselected'
];

events.forEach(eventName => {
  window.addEventListener(`autopictura${eventName}`, (event) => {
    console.log(`autopictura${eventName}`, event.detail);
  });
});