{"id":16055,"date":"2018-09-17T12:49:53","date_gmt":"2018-09-17T11:49:53","guid":{"rendered":"https:\/\/marvel7077.wpengine.com\/?p=16055"},"modified":"2020-11-12T12:48:33","modified_gmt":"2020-11-12T12:48:33","slug":"how-we-optimised-prototype-image-loading","status":"publish","type":"post","link":"https:\/\/marvelapp.com\/blog\/how-we-optimised-prototype-image-loading\/","title":{"rendered":"How We Optimised Prototype Image Loading"},"content":{"rendered":"<p class=\"pageWrap pageWrap--s marginBottom-m paddingBottom-s c-slate lineHeight-l fontSize-l fontWeight-3 breakPointM-fontSize-xl breakPointM-lineHeight-xl\">To build prototypes in <a class=\"link link--blue fontWeight-4\"href=\"https:\/\/marvelapp.com\/features\/prototyping\">Marvel<\/a>, all you need to do is import your designs. Whether they're low-fi wireframes you drew on paper or hi-fi prototypes you built in Sketch, you can add these as static images to a project and make them interactive by adding hotspots. These hotspots link together your designs to create a flow which in the end will feel like a real life website or app mock up. <\/p>\n<p class=\"pageWrap pageWrap--s marginBottom-m paddingBottom-s c-slate lineHeight-l fontSize-l fontWeight-3 breakPointM-fontSize-xl breakPointM-lineHeight-xl\">Historically, our prototype player used to load all images upfront, which works well with small prototypes, but worsens the user experience the bigger a prototype gets. Loading this way meant that despite opening a prototype and only going through the first few screens, all of the images would get downloaded at once, regardless of how many screens they viewed. <\/p>\n<p class=\"pageWrap pageWrap--s marginBottom-m paddingBottom-s c-slate lineHeight-l fontSize-l fontWeight-3 breakPointM-fontSize-xl breakPointM-lineHeight-xl\">We recently decided it's about time we optimised our image loading in the prototype viewer and rethink our current approach. In this blog post we'll explain how we've improved image loading since.<\/p>\n<h2 class=\"pageWrap pageWrap--s marginTop-xl marginBottom-l c-black lineHeight-xl fontSize-xl fontWeight-5 breakPointM-lineHeight-xxl breakPointM-fontSize-xxl\" style=\"margin-left: auto; margin-right: auto;\">Why loading all images upfront is not a great idea<\/h2>\n<p class=\"pageWrap pageWrap--s marginBottom-m paddingBottom-s c-slate lineHeight-l fontSize-l fontWeight-3 breakPointM-fontSize-xl breakPointM-lineHeight-xl\">There's a number of reasons why this doesn't work well. From a business perspective, the more images we need to serve to the browser, the more our bandwidth costs increase. But our pockets aren't the only ones getting hurt, downloading all the images is also costly for users. Accessing Marvel via mobile connection, having limited data, and having to download a few MBs of images every time you view a prototype adds up very quickly and is simply wasteful.<\/p>\n<p class=\"pageWrap pageWrap--s marginBottom-m paddingBottom-s c-slate lineHeight-l fontSize-l fontWeight-3 breakPointM-fontSize-xl breakPointM-lineHeight-xl\"> And it's not just downloading images that gets costly, every image needs to be decoded and rendered by the browser, which can lead to wasted system resources.<\/p>\n<p class=\"pageWrap pageWrap--s marginBottom-m paddingBottom-s c-slate lineHeight-l fontSize-l fontWeight-3 breakPointM-fontSize-xl breakPointM-lineHeight-xl\">Along with those reasons to find a new solution, there is another bonus of optimising our image loading. In the past if you clicked a hotspot taking you to a new screen that is not close to the previous one, there is a good chance that the image would not have loaded yet. With our new approach, all of the images you might need in the immediate future get downloaded first.<\/p>\n<h2 class=\"pageWrap pageWrap--s marginTop-xl marginBottom-l c-black lineHeight-xl fontSize-xl fontWeight-5 breakPointM-lineHeight-xxl breakPointM-fontSize-xxl\" style=\"margin-left: auto; margin-right: auto;\">Graph to the rescue<\/h2>\n<p class=\"pageWrap pageWrap--s marginBottom-m paddingBottom-s c-slate lineHeight-l fontSize-l fontWeight-3 breakPointM-fontSize-xl breakPointM-lineHeight-xl\">All of our screens are stored as a graph, which we can use to our advantage. But what does that mean exactly? <\/p>\n<p class=\"pageWrap pageWrap--s marginBottom-m paddingBottom-s c-slate lineHeight-l fontSize-l fontWeight-3 breakPointM-fontSize-xl breakPointM-lineHeight-xl\"><span class=\"long-quote\">A graph is a data structure that represents relationships between the stored items.<\/span><\/p>\n<p class=\"pageWrap pageWrap--s marginBottom-m paddingBottom-s c-slate lineHeight-l fontSize-l fontWeight-3 breakPointM-fontSize-xl breakPointM-lineHeight-xl\">Graphs can be directed or undirected. Facebook relationships are a good example of undirected graph. You send a friend request to someone and once they accept, you become friends and it's both ways. Instagram on the other hand is an example of a directed graph. You can follow someone on there, but they don't have to follow you back for you to be able to see their posts.<\/p>\n<h2 class=\"pageWrap pageWrap--s marginTop-xl marginBottom-l c-black lineHeight-xl fontSize-xl fontWeight-5 breakPointM-lineHeight-xxl breakPointM-fontSize-xxl\" style=\"margin-left: auto; margin-right: auto;\">How does this apply to our screens?<\/h2>\n<p class=\"pageWrap pageWrap--s marginBottom-m paddingBottom-s c-slate lineHeight-l fontSize-l fontWeight-3 breakPointM-fontSize-xl breakPointM-lineHeight-xl\">In order to improve image loading, we decided to utilise a concept called breadth-first-search. Breadth-first-search can be simply illustrated by different approaches to acquiring new skills - some people learn as much as they can about a certain subject before they move on to another, whereas others like to cover as broad spectrum of different skills going on at the same time as possible. The latter approach is an analogy for breadth-first-search. <\/p>\n<p class=\"pageWrap pageWrap--s marginBottom-m paddingBottom-s c-slate lineHeight-l fontSize-l fontWeight-3 breakPointM-fontSize-xl breakPointM-lineHeight-xl\">In context of loading our screens, this works as follows:<\/p>\n<ul class=\"pageWrap pageWrap--s list list--unordered marginBottom-l lineHeight-l fontSize-l fontWeight-3 breakPointM-fontSize-xl breakPointM-lineHeight-xl\">\n<li>For each current screen, we load its next and previous screens, as well as any they link to through hotspots<\/li>\n<li>This applies to a depth of 3, meaning that once we've found the next, previous and linked screens, we repeat the whole process for those screens until we've covered three depths<\/li>\n<li>We use queues to keep track of the screens that need to be handled next<\/li>\n<\/ul>\n<h2 class=\"pageWrap pageWrap--s marginTop-xl marginBottom-l c-black lineHeight-xl fontSize-xl fontWeight-5 breakPointM-lineHeight-xxl breakPointM-fontSize-xxl\" style=\"margin-left: auto; margin-right: auto;\">Let's look at a visual example<\/h2>\n<p class=\"pageWrap pageWrap--s marginBottom-m paddingBottom-s c-slate lineHeight-l fontSize-l fontWeight-3 breakPointM-fontSize-xl breakPointM-lineHeight-xl\">It's all good and well explaining this in a few bullet points, but let's give this a little more clarity with an example! <\/p>\n<p class=\"pageWrap pageWrap--s marginBottom-m paddingBottom-s c-slate lineHeight-l fontSize-l fontWeight-3 breakPointM-fontSize-xl breakPointM-lineHeight-xl\">Below, you can see that we retrieve the previous (P), next(N) and linked (L) images for our current image which sits at the top. It so happens that next image is also the linked image in this case. This is our depth of one, done.<\/p>\n<p class=\"pageWrap pageWrap--s marginBottom-m paddingBottom-s c-slate lineHeight-l fontSize-l fontWeight-3 breakPointM-fontSize-xl breakPointM-lineHeight-xl\"><a class=\"link link--blue fontWeight-4\"href=\"https:\/\/marvel7077.wpengine.com\/wp-content\/uploads\/2018\/09\/screen-1.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/marvel7077.wpengine.com\/wp-content\/uploads\/2018\/09\/screen-1.png\" alt=\"\" width=\"2130\" height=\"848\" class=\"alignnone size-full wp-image-16057\" srcset=\"https:\/\/marvelapp.com\/wp-content\/uploads\/2018\/09\/screen-1.png 2130w, https:\/\/marvelapp.com\/wp-content\/uploads\/2018\/09\/screen-1-600x239.png 600w, https:\/\/marvelapp.com\/wp-content\/uploads\/2018\/09\/screen-1-768x306.png 768w, https:\/\/marvelapp.com\/wp-content\/uploads\/2018\/09\/screen-1-1500x597.png 1500w\" sizes=\"auto, (max-width: 2130px) 100vw, 2130px\" \/><\/a><\/p>\n<p class=\"pageWrap pageWrap--s marginBottom-m paddingBottom-s c-slate lineHeight-l fontSize-l fontWeight-3 breakPointM-fontSize-xl breakPointM-lineHeight-xl\">Moving onto depth of two.<\/p>\n<p class=\"pageWrap pageWrap--s marginBottom-m paddingBottom-s c-slate lineHeight-l fontSize-l fontWeight-3 breakPointM-fontSize-xl breakPointM-lineHeight-xl\">We can see that there are two screens at the depth of two, so let's get the previous, next and linked screens for the first of them. You can see (below) that the next screen of the current screen happens to be the image that we started with.<\/p>\n<p class=\"pageWrap pageWrap--s marginBottom-m paddingBottom-s c-slate lineHeight-l fontSize-l fontWeight-3 breakPointM-fontSize-xl breakPointM-lineHeight-xl\"><a class=\"link link--blue fontWeight-4\"href=\"https:\/\/marvel7077.wpengine.com\/wp-content\/uploads\/2018\/09\/screen-2.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/marvel7077.wpengine.com\/wp-content\/uploads\/2018\/09\/screen-2.png\" alt=\"\" width=\"2128\" height=\"1315\" class=\"alignnone size-full wp-image-16058\" srcset=\"https:\/\/marvelapp.com\/wp-content\/uploads\/2018\/09\/screen-2.png 2128w, https:\/\/marvelapp.com\/wp-content\/uploads\/2018\/09\/screen-2-600x371.png 600w, https:\/\/marvelapp.com\/wp-content\/uploads\/2018\/09\/screen-2-768x475.png 768w, https:\/\/marvelapp.com\/wp-content\/uploads\/2018\/09\/screen-2-1500x927.png 1500w\" sizes=\"auto, (max-width: 2128px) 100vw, 2128px\" \/><\/a><\/p>\n<p class=\"pageWrap pageWrap--s marginBottom-m paddingBottom-s c-slate lineHeight-l fontSize-l fontWeight-3 breakPointM-fontSize-xl breakPointM-lineHeight-xl\">Now we're done with the first screen, let's then move onto the next one and get its previous, next and linked screens. By now, you're probably beginning to see the pattern.<\/p>\n<p class=\"pageWrap pageWrap--s marginBottom-m paddingBottom-s c-slate lineHeight-l fontSize-l fontWeight-3 breakPointM-fontSize-xl breakPointM-lineHeight-xl\"><a class=\"link link--blue fontWeight-4\"href=\"https:\/\/marvel7077.wpengine.com\/wp-content\/uploads\/2018\/09\/screen-3.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/marvel7077.wpengine.com\/wp-content\/uploads\/2018\/09\/screen-3.png\" alt=\"\" width=\"2133\" height=\"1291\" class=\"alignnone size-full wp-image-16059\" srcset=\"https:\/\/marvelapp.com\/wp-content\/uploads\/2018\/09\/screen-3.png 2133w, https:\/\/marvelapp.com\/wp-content\/uploads\/2018\/09\/screen-3-600x363.png 600w, https:\/\/marvelapp.com\/wp-content\/uploads\/2018\/09\/screen-3-768x465.png 768w, https:\/\/marvelapp.com\/wp-content\/uploads\/2018\/09\/screen-3-1500x908.png 1500w\" sizes=\"auto, (max-width: 2133px) 100vw, 2133px\" \/><\/a><\/p>\n<p class=\"pageWrap pageWrap--s marginBottom-m paddingBottom-s c-slate lineHeight-l fontSize-l fontWeight-3 breakPointM-fontSize-xl breakPointM-lineHeight-xl\">After checking all depth 2 images, we arrive at 3 depths of images. Et voil\u00e0!<\/p>\n<p class=\"pageWrap pageWrap--s marginBottom-m paddingBottom-s c-slate lineHeight-l fontSize-l fontWeight-3 breakPointM-fontSize-xl breakPointM-lineHeight-xl\">Of course, we also need to make sure that every time you navigate to a new screen, we start this process all over again. However, we don't calculate the related screens if we've already done it for a particular screen, saving some time on unnecessarily traversing the graph, and saving your hard earned cash by not downloading the images multiple times.<\/p>\n<p class=\"pageWrap pageWrap--s marginBottom-m paddingBottom-s c-slate lineHeight-l fontSize-l fontWeight-3 breakPointM-fontSize-xl breakPointM-lineHeight-xl\">We hope you've enjoyed this quick rundown of how we've improved our image loading. We've already noticed a massive difference and seen an improvement in the loading speed of our prototypes, especially the big ones.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>To build prototypes in Marvel, all you need to do is import your designs. Whether they&#8217;re low-fi wireframes you drew on paper or hi-fi prototypes you built in Sketch, you can add these as static images to a project and make them interactive by adding hotspots. These hotspots link together your designs to create a flow which in the end&#8230; <a class=\"link link--blue fontWeight-4\" href=\"https:\/\/marvelapp.com\/blog\/how-we-optimised-prototype-image-loading\/\">Read More &#65515;<\/a><\/p>\n","protected":false},"author":250,"featured_media":16063,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[496],"tags":[],"class_list":["post-16055","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-prototyping-design-thoughts"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v15.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<meta name=\"description\" content=\"We recently decided it&#039;s about time we optimised our image loading in the prototype viewer and rethink our current approach. Here&#039;s how we did it!\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/marvelapp.com\/blog\/how-we-optimised-prototype-image-loading\/\" \/>\n<meta property=\"og:locale\" content=\"en_GB\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How We Optimised Prototype Image Loading | Marvel Blog\" \/>\n<meta property=\"og:description\" content=\"We recently decided it&#039;s about time we optimised our image loading in the prototype viewer and rethink our current approach. Here&#039;s how we did it!\" \/>\n<meta property=\"og:url\" content=\"https:\/\/marvelapp.com\/blog\/how-we-optimised-prototype-image-loading\/\" \/>\n<meta property=\"og:site_name\" content=\"Marvel Blog\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/marvelapp\" \/>\n<meta property=\"article:published_time\" content=\"2018-09-17T11:49:53+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2020-11-12T12:48:33+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/marvelapp.com\/wp-content\/uploads\/2018\/09\/graphimage.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1601\" \/>\n\t<meta property=\"og:image:height\" content=\"701\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@marvelapp\" \/>\n<meta name=\"twitter:site\" content=\"@marvelapp\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\">\n\t<meta name=\"twitter:data1\" content=\"4 minutes\">\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https:\/\/marvelapp.com\/blog\/#organization\",\"name\":\"Marvel\",\"url\":\"https:\/\/marvelapp.com\/blog\/\",\"sameAs\":[\"https:\/\/www.facebook.com\/marvelapp\",\"https:\/\/www.instagram.com\/marvelapp\/\",\"https:\/\/www.linkedin.com\/company\/marvel-app\/\",\"https:\/\/twitter.com\/marvelapp\"],\"logo\":{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/marvelapp.com\/blog\/#logo\",\"inLanguage\":\"en-GB\",\"url\":\"https:\/\/marvelapp.com\/wp-content\/uploads\/2018\/06\/Logo-Light.png\",\"width\":1605,\"height\":1130,\"caption\":\"Marvel\"},\"image\":{\"@id\":\"https:\/\/marvelapp.com\/blog\/#logo\"}},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/marvelapp.com\/blog\/#website\",\"url\":\"https:\/\/marvelapp.com\/blog\/\",\"name\":\"Marvel Blog\",\"description\":\"Ideas and words on user experience, design, collaboration and more\",\"publisher\":{\"@id\":\"https:\/\/marvelapp.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":\"https:\/\/marvelapp.com\/blog\/?s={search_term_string}\",\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-GB\"},{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/marvelapp.com\/blog\/how-we-optimised-prototype-image-loading\/#primaryimage\",\"inLanguage\":\"en-GB\",\"url\":\"https:\/\/marvelapp.com\/wp-content\/uploads\/2018\/09\/graphimage.png\",\"width\":\"1601\",\"height\":\"701\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/marvelapp.com\/blog\/how-we-optimised-prototype-image-loading\/#webpage\",\"url\":\"https:\/\/marvelapp.com\/blog\/how-we-optimised-prototype-image-loading\/\",\"name\":\"How We Optimised Prototype Image Loading | Marvel Blog\",\"isPartOf\":{\"@id\":\"https:\/\/marvelapp.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/marvelapp.com\/blog\/how-we-optimised-prototype-image-loading\/#primaryimage\"},\"datePublished\":\"2018-09-17T11:49:53+00:00\",\"dateModified\":\"2020-11-12T12:48:33+00:00\",\"description\":\"We recently decided it's about time we optimised our image loading in the prototype viewer and rethink our current approach. Here's how we did it!\",\"inLanguage\":\"en-GB\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/marvelapp.com\/blog\/how-we-optimised-prototype-image-loading\/\"]}]},{\"@type\":\"Article\",\"@id\":\"https:\/\/marvelapp.com\/blog\/how-we-optimised-prototype-image-loading\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/marvelapp.com\/blog\/how-we-optimised-prototype-image-loading\/#webpage\"},\"author\":{\"@id\":\"https:\/\/marvelapp.com\/blog\/#\/schema\/person\/83130333a7242d38d224771474ba76db\"},\"headline\":\"How We Optimised Prototype Image Loading\",\"datePublished\":\"2018-09-17T11:49:53+00:00\",\"dateModified\":\"2020-11-12T12:48:33+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/marvelapp.com\/blog\/how-we-optimised-prototype-image-loading\/#webpage\"},\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/marvelapp.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/marvelapp.com\/blog\/how-we-optimised-prototype-image-loading\/#primaryimage\"},\"articleSection\":\"Design &amp; Prototyping\",\"inLanguage\":\"en-GB\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/marvelapp.com\/blog\/how-we-optimised-prototype-image-loading\/#respond\"]}]},{\"@type\":\"Person\",\"@id\":\"https:\/\/marvelapp.com\/blog\/#\/schema\/person\/83130333a7242d38d224771474ba76db\",\"name\":\"Kas Fijolek\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/marvelapp.com\/blog\/#personlogo\",\"inLanguage\":\"en-GB\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/d7b35e9bfb8236abf338da7e9bb5f9ebf07d151f60684352af8a52c41dbc546e?s=96&d=mm&r=g\",\"caption\":\"Kas Fijolek\"},\"description\":\"Developer @ Marvel\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","_links":{"self":[{"href":"https:\/\/marvelapp.com\/blog\/wp-json\/wp\/v2\/posts\/16055","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/marvelapp.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/marvelapp.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/marvelapp.com\/blog\/wp-json\/wp\/v2\/users\/250"}],"replies":[{"embeddable":true,"href":"https:\/\/marvelapp.com\/blog\/wp-json\/wp\/v2\/comments?post=16055"}],"version-history":[{"count":0,"href":"https:\/\/marvelapp.com\/blog\/wp-json\/wp\/v2\/posts\/16055\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/marvelapp.com\/blog\/wp-json\/wp\/v2\/media\/16063"}],"wp:attachment":[{"href":"https:\/\/marvelapp.com\/blog\/wp-json\/wp\/v2\/media?parent=16055"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/marvelapp.com\/blog\/wp-json\/wp\/v2\/categories?post=16055"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/marvelapp.com\/blog\/wp-json\/wp\/v2\/tags?post=16055"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}