Default Integration Script
Simplified implementation of Piano consists of placing a single JavaScript code snippet on the web site. That code snippet can be found in Piano dashboard under Products→ Composer → Integrate, and it looks like this:
<script>
(function (src) {
var script = document.createElement("script");
script.type = "text/javascript";
script.async = true;
script.src = src;
var firstScript = document.getElementsByTagName("script")[0];
firstScript.parentNode.insertBefore(script, firstScript);
})("https://experience.tinypass.com/xbuilder/experience/load?aid=<AID>");
</script>
Executing this snippet will load additional code that serves 3 purposes:
-
Load
tinypass.min.jslibrary, the main library for any Piano-related functionality on the website. -
Initialize settings for the Piano application, i.e. application id, environment, and user provider.
-
Execute Composer.
However, in some cases Piano clients may find that loading this code over the network might be unnecessary and as such could cause an unnecessary increase of experience delivery times during page load. Piano scripts can be implemented without the extra loader to improve performance. To achieve this, three actions need to be performed to match the purposes of the original integration script:
-
Load
tinypass.min.jsdirectly in the<head>tag, to make sure it starts loading as soon as possible. -
Initialize settings.
-
Execute Composer in the
<body>tag.
The recommended implementation pattern below explains this approach in detail.
Why Performance Issues Happen
Slow or unreliable Experience execution is typically caused by one (or more) of these factors:
-
Late loading of
tinypass.min.js(for example, loading at the end of<body>), which delays Experience execution. -
Incorrect sequencing (executing Composer before the Piano library and settings are ready).
-
Too many overlapping Experiences or overly complex templates, which increases client-side work.
-
Duplicate script loads (loading Piano more than once), which can cause performance issues and erratic behavior.
-
Third-party dependencies (CMP/consent lookups, monitoring tags, large images/scripts) delaying network or main-thread availability—especially on slow mobile networks.
-
Browser constraints (limited parallel network requests), making "extra" loader scripts a bottleneck.
Recommended Implementation Pattern
Load the Piano Library Early (in <head>)
Place tinypass.min.js in the <head> so it starts downloading as early as possible:
<script type="text/javascript" async src="https://cdn.tinypass.com/api/tinypass.min.js"></script>
Optional: Preconnect to Piano Hosts
preconnect can reduce connection setup latency (DNS/TCP/TLS), especially on first-page loads. Add these lines in your <head> before the script tag:
<link rel="preconnect" href="https://cdn.tinypass.com">
<link rel="preconnect" href="https://experience.tinypass.com">
Split Your Integration: Loading → Settings → Execution
For best performance and predictable behavior, structure your implementation as three logical steps:
-
Loading: Fetch
tinypass.min.js(in<head>). -
Settings/initialization: Configure Piano (AID, endpoint, user provider, tags, custom variables, etc.) once the library is available.
-
Execution: Initialize/execute Experiences (Composer) after settings are applied.
This split reduces perceived latency and avoids cases where execution happens before configuration is ready.
Execute Experiences as Early as Practical
A safe pattern is:
-
Load
tinypass.min.jsearly (in<head>). -
In the page body (or immediately after your settings are defined), run initialization and then execute Experiences.
An example of a performance-optimized implementation applying all three steps:
<script type="text/javascript" async src="https://cdn.tinypass.com/api/tinypass.min.js"></script>
<script type="text/javascript">
tp = window.tp || [];
tp.push(["setAid", '<AID>']);
// Add this line for the Composer 1x feature. It will set your Site ID
tp.push(["setCxenseSiteId", "<Site_ID>"])
// Use https://sandbox.tinypass.com/api/v3 for sandbox
tp.push(["setEndpoint", 'https://buy.tinypass.com/api/v3']);
// In most cases this is false unless instructed otherwise
tp.push(["setUseTinypassAccounts", false]);
// Set to "true" if application uses Piano ID
tp.push(["setUsePianoIdUserProvider", true]);
</script>
<script type="text/javascript">
tp = window.tp || [];
tp.push(["init", function () {
tp.experience.init();
}])
</script>
Ensure Piano Is Initialized Before Executing
If your code depends on initialization state, confirm initialization is complete before executing. Some implementations rely on tp.isInitialized being true after the library and settings are properly applied.
Asynchronous Loading: What to Do (and What to Avoid)
-
Do load
tinypass.min.jsasynchronously to reduce render blocking. -
Don't make everything asynchronous without controlling sequencing. If both the library and your execution code are async with no coordination, Experiences may not run until very late (sometimes effectively "after the site finishes loading"), which users perceive as a delay.
If you dynamically inject the script (SPA frameworks, tag managers, etc.), ensure that:
-
The injection happens early in the lifecycle.
-
Your settings/execution code runs only after the Piano library is ready.
Avoid Common Integration Mistakes
Prevent Duplicate Loads
Verify tinypass.min.js is loaded once per page. Duplicate loads can significantly impact performance and can produce unpredictable behavior.
How to check:
-
View page source / DevTools Network tab for multiple
tinypass.min.jsrequests. -
Check console logs for re-initialization patterns.
Reduce Overlapping or Excessive Experiences
Performance degrades as you increase:
-
The number of Experiences that can match the same page/user.
-
The complexity/size of templates.
-
The number of containers rendered.
If possible:
-
Limit Experiences on high-traffic or critical pages (homepages, landing pages).
-
Split very large Experiences into smaller ones with tighter targeting.
-
Eliminate overlaps where multiple Experiences compete to run.
Be Cautious with Extra Integration/Loader Scripts
On slow connections, additional wrapper scripts can delay tinypass.min.js because browsers limit parallel downloads. When performance is critical, prefer loading tinypass.min.js and your configuration directly rather than via additional loader layers.
Consent (CMP) Can Be a Major Bottleneck
A frequent cause of delayed Experience execution is waiting for consent data from a CMP.
If your flow requires consent before execution, consider optimizing how consent state is retrieved. One approach is to cache consent state in a first-party cookie after the first visit and reuse it on subsequent page loads, so Piano doesn't wait on a remote CMP request each time.
Implement consent caching only if it aligns with your compliance requirements and your CMP's policies.
Troubleshooting Slow Execution with DevTools
Use Network Timing to Identify the Delay Source
-
Open Chrome DevTools → Network.
-
Reload the page with Disable cache enabled.
-
Look for:
-
tinypass.min.jsdownload start time. -
Experience-related calls (often identifiable by
executein the request name/URL).
-
Filter by execute to quickly locate Experience execution requests and compare their timing against overall page load milestones.
Check for Heavy Competing Resources
Large background images, long-running third-party scripts, and monitoring tags can block or delay execution—especially on mobile/low bandwidth. Reduce image sizes and defer non-critical scripts where possible.
Performance Targets and Expectations
Actual load time varies by page complexity, number of Experiences, consent workflow, and network conditions. As a general guideline, well-optimized implementations often aim for Piano templates/experiences to become visible within ~1–3 seconds on typical connections, but slower networks may require additional optimizations (preconnect, reduced experience overlap, lighter pages).
Special Notes
-
Safari / ITP considerations: Safari's Intelligent Tracking Prevention can affect cookie behavior and therefore Experience triggering. If Experiences behave inconsistently on reload, validate cookie setting/availability and confirm your script sequencing is correct.
-
SDK size and dependencies: The Piano SDK size and its dependent requests can impact load time on slower networks. Optimize what you control (placement, sequencing, overlap) and monitor network waterfalls to understand real-world impact.