<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5258244231997226159</id><updated>2026-03-15T05:06:54.745-03:00</updated><category term="WebAssembly"/><category term="wasm"/><category term="Emscripten"/><category term="JavaScript"/><category term="Manning Publications"/><category term="ConFoo"/><category term="Dovico"/><category term="WebAssembly in Action"/><category term="book"/><category term="Uno Platform"/><category term="module"/><category term="C#"/><category term="Internet Explorer"/><category term="Manning"/><category term="Montreal"/><category term="WASI"/><category term="Windows 8"/><category term="projects"/><category term="DOMParser"/><category term="Dovico Timesheet"/><category term="Firefox"/><category term="HTML 5 Web Workers"/><category term="HTML5"/><category term="IndexedDB"/><category term="Internet Explorer 9"/><category term="State of WebAssembly"/><category term="Web Workers"/><category term="WebAssembly System Interface"/><category term="WebAssembly.instantiate"/><category term="WebView"/><category term="Windows Store app"/><category term="XMLHttpRequest"/><category term="application/wasm"/><category term="debugging"/><category term="fetch"/><category term="thread"/><category term=".NET"/><category term=".NET Core"/><category term="Blazor"/><category term="C"/><category term="C++"/><category term="COEP"/><category term="COOP"/><category term="Cross-Origin-Embedder-Policy"/><category term="Cross-Origin-Opener-Policy"/><category term="DZone"/><category term="Developer Tools"/><category term="Docker"/><category term="GitHub"/><category term="IIS"/><category term="JSON"/><category term="MSXML"/><category term="Metro"/><category term="Microsoft Project Server 2010"/><category term="Python"/><category term="Refcardz"/><category term="SIDE_MODULE"/><category term="SIMD"/><category term="SharedArrayBuffer"/><category term="SharedWorker"/><category term="Slack"/><category term="Standards mode"/><category term="WebAssembly.compile"/><category term="addEventListener"/><category term="cache"/><category term="caching"/><category term="ccall"/><category term="class"/><category term="crossorigin"/><category term="cwrap"/><category term="garbage collection"/><category term="importScripts"/><category term="performance"/><category term="postMessage"/><category term="singleton"/><category term="task templates"/><category term="tasks"/><category term="wasm-gc"/><category term="2023"/><category term="2024"/><category term="@inject"/><category term="@ref"/><category term="AOT"/><category term="ASP.NET"/><category term="ASP.NET Page Life Cycle"/><category term="AllowedScriptNotifyUris"/><category term="Android"/><category term="ApplicationData"/><category term="Array"/><category term="Azure"/><category term="BECanvas"/><category term="Blob"/><category term="Canvas"/><category term="Catch"/><category term="CloseEvent"/><category term="Compatibility View"/><category term="Console tab"/><category term="Cookies"/><category term="DYNAMICTOP_PTR"/><category term="DataChanged"/><category term="DefaultRequestHeaders"/><category term="DocType"/><category term="Document Compatibility Modes"/><category term="EMSCRIPTEN_KEEPALIVE"/><category term="EM_ASM"/><category term="EXPORT_ES6"/><category term="ElectricSQL"/><category term="Error object"/><category term="Finally"/><category term="Firebug"/><category term="Google Earth"/><category term="HTML 5 Web Storage"/><category term="HTML 5 WebSocket API"/><category term="HTML5 IndexedDB"/><category term="Halifax"/><category term="Hotel Bonaventure"/><category term="HttpClient"/><category term="Hyperlight"/><category term="IE 10"/><category term="IE 9"/><category term="Inline WebAssembly"/><category term="IntelliSense"/><category term="Internet Explorer 10"/><category term="Internet Explorer 8"/><category term="Internet Explorer Developer Tools"/><category term="JSFiddle"/><category term="JSONP"/><category term="JavaScript Object Notation"/><category term="Linux containers"/><category term="MEAP"/><category term="MIME Type"/><category term="MODULARIZE"/><category term="Memory64"/><category term="MessageEvent"/><category term="Microsoft Project 2010"/><category term="Microsoft experts and consultants"/><category term="NIST"/><category term="NO_EXIT_RUNTIME"/><category term="Navigate Backwards"/><category term="Navigate Forward"/><category term="Object"/><category term="ObjectForScripting"/><category term="OnAfterRenderAsync"/><category term="OnInitializedAsync"/><category term="PGLite"/><category term="Project 2007 compatibility mode"/><category term="Promise"/><category term="Quick Assign"/><category term="RESERVED_FUNCTION_POINTERS"/><category term="RoamingSettings"/><category term="SQLite"/><category term="Safari"/><category term="ScriptNotify"/><category term="Settings Charm"/><category term="Settings flyout"/><category term="SignalDataChanged"/><category term="SimpleHTTPServer"/><category term="Storage event"/><category term="Task.Delay"/><category term="TechDays 2010"/><category term="Thread.Sleep"/><category term="Throw"/><category term="Timesheet"/><category term="Try"/><category term="USE_ES6_IMPORT_META"/><category term="UnoConf"/><category term="View Call Hierarchy"/><category term="Visual Studio"/><category term="Vue.js"/><category term="WasmFiddle"/><category term="Wasmtime"/><category term="WebAssembly.module"/><category term="WebBrowser"/><category term="WebViewBrush"/><category term="Windows Store"/><category term="Windows containers"/><category term="Worker"/><category term="X-UA-Compatible"/><category term="a.out.wasm"/><category term="achieve"/><category term="addFunction"/><category term="ahead-of-time compile"/><category term="allTabs"/><category term="assign"/><category term="assignments"/><category term="attach to process"/><category term="attachEvent"/><category term="attributes"/><category term="background threads"/><category term="certification requirement 4.1"/><category term="chrome"/><category term="close"/><category term="communication"/><category term="component model"/><category term="createObjectURL"/><category term="ctrlTab"/><category term="debugger command"/><category term="dedicated workers"/><category term="desktop"/><category term="developers"/><category term="download"/><category term="edit"/><category term="employees"/><category term="error"/><category term="error log"/><category term="events"/><category term="exception"/><category term="extern"/><category term="flicker"/><category term="full-duplex"/><category term="function pointers"/><category term="goal"/><category term="habit"/><category term="half-duplex"/><category term="http.server"/><category term="image"/><category term="img tag"/><category term="import"/><category term="import.meta.url"/><category term="importing JavaScript"/><category term="inline workers"/><category term="instance"/><category term="instantiate"/><category term="instantiateWasm"/><category term="libjxl"/><category term="local-first"/><category term="localStorage"/><category term="long-polling"/><category term="macros"/><category term="memory"/><category term="memoryBase"/><category term="menu"/><category term="migrate"/><category term="multiple shared workers"/><category term="namespace"/><category term="network"/><category term="onconnect"/><category term="onerror"/><category term="onmessage"/><category term="onmessage of shared worker"/><category term="parseFromString"/><category term="phone"/><category term="pinned site"/><category term="plans"/><category term="polling"/><category term="port"/><category term="privacy policy"/><category term="project"/><category term="prototype"/><category term="publish"/><category term="rearranged"/><category term="removeFunction"/><category term="render"/><category term="repository"/><category term="require-corp"/><category term="resolution"/><category term="resources"/><category term="responseText"/><category term="responseXML"/><category term="rethrow"/><category term="review"/><category term="same origin policy"/><category term="same-origin"/><category term="scope of shared workers"/><category term="script injection"/><category term="script tag"/><category term="sessionStorage"/><category term="settings"/><category term="share"/><category term="shared workers"/><category term="speaking"/><category term="static web app"/><category term="status bar"/><category term="streaming"/><category term="tab preview"/><category term="table"/><category term="tableBase"/><category term="tablet"/><category term="tail calls"/><category term="teams"/><category term="terminate"/><category term="timouts"/><category term="tips"/><category term="transferable objects"/><category term="unassign"/><category term="web server"/><category term="window.external.notify"/><title type='text'>C. Gerard Gallant</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>63</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-726727916452210516</id><published>2026-03-07T19:05:00.000-04:00</published><updated>2026-03-07T19:05:15.316-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ConFoo"/><category scheme="http://www.blogger.com/atom/ns#" term="Montreal"/><category scheme="http://www.blogger.com/atom/ns#" term="Slack"/><category scheme="http://www.blogger.com/atom/ns#" term="speaking"/><title type='text'>ConFoo 2026 in Review</title><content type='html'>&lt;span style=&quot;display:none;&quot;&gt;A short article talking about my experience applying to speak at ConFoo, attending the conference, and being given an opportunity to speak after all.
  
  &lt;img alt=&quot;&quot; border=&quot;0&quot; height=&quot;400&quot; data-original-height=&quot;3300&quot; data-original-width=&quot;2686&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUqDQYMI2jcrt8YZbkBfslUgtxi3F5UuBgOpw0HyoXjumn3itvafKYNrvR48On2MPMBdTfO5W30YpscGe9tM9Ts0vPg372XbZX-faZc5tRO5R8dNQ-wk4os7rB4tfNuQxvrvEWOKsZHqu3AuzIRGz3UIp-752reO0MbLXHCQ4EsmWmoQ7PXDdJAoJfbeo/s400/attendee_speaker_badges.jpg&quot; alt=&quot;My Confoo attendee and speaker badges&quot;/&gt;
&lt;/span&gt;

&lt;div style=&quot;color:black;font-family:arial;&quot;&gt;

  &lt;div class=&quot;separator&quot; style=&quot;clear:both;&quot;&gt;
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOcTt8c6ykEIXeEjHULhk7YyuW2BDX1kxBuTGb-z28GIKMHQv7oetn0dNSdgpg1ewN4WtzLNB9kkbPFxOi7bCompEiv-UIf7-eXX7n2d8vndUzCZpJ8qooM_fbM1lILKcRHiH5l_AcMKsleW3lsu7jzW3DIST5QTTPxy_bksQud5kp8PxuyPmD2bmKpwc/s200/confoologo.png&quot; style=&quot;display:block;padding:0 0 1em 0.5em;text-align:center;clear:right;float:right;&quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;200&quot; data-original-height=&quot;61&quot; data-original-width=&quot;200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOcTt8c6ykEIXeEjHULhk7YyuW2BDX1kxBuTGb-z28GIKMHQv7oetn0dNSdgpg1ewN4WtzLNB9kkbPFxOi7bCompEiv-UIf7-eXX7n2d8vndUzCZpJ8qooM_fbM1lILKcRHiH5l_AcMKsleW3lsu7jzW3DIST5QTTPxy_bksQud5kp8PxuyPmD2bmKpwc/s200/confoologo.png&quot; alt=&quot;&quot;/&gt;
    &lt;/a&gt;
  &lt;/div&gt;
  
  &lt;a href=&quot;https://confoo.ca/en/2026&quot; target=&quot;_blank&quot;&gt;ConFoo&lt;/a&gt; is one of Canada’s biggest developer conferences drawing in over 100 speakers from around the world. The speakers converge in Montreal to share their knowledge by giving close to 200 talks over the course of three days.
  
  &lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiflFLTW3z5j-GK1SGrebrjFMxdhDisF-mqr8qaJAPGGGH83qWR-GLGJNfKGbaRhaZpBJyiy9X5VxIF2D8Uw1kyAbAOcsKMbklchGuo9uZD3dPTVmkQoHLv6rPxih6HJR-IUZFkZkXyfOokLRhE8W978etykjJ7qnizlD9e2Cks2nrl3rvAJojUBGeSrMM/s4000/GareCentrale.jpg&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;500&quot; data-original-height=&quot;2482&quot; data-original-width=&quot;4000&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiflFLTW3z5j-GK1SGrebrjFMxdhDisF-mqr8qaJAPGGGH83qWR-GLGJNfKGbaRhaZpBJyiy9X5VxIF2D8Uw1kyAbAOcsKMbklchGuo9uZD3dPTVmkQoHLv6rPxih6HJR-IUZFkZkXyfOokLRhE8W978etykjJ7qnizlD9e2Cks2nrl3rvAJojUBGeSrMM/s320/GareCentrale.jpg&quot; alt=&quot;The inside of Gare Centrale (Central Station) next to the conference hotel in Montreal&quot;/&gt;
      &lt;br /&gt;Gare Centrale (Central Station) next to the conference hotel in Montreal
    &lt;/a&gt;
  &lt;/div&gt;
  
  Every year I submit proposals to speak at ConFoo and I’ve been fortunate because I&#39;ve been selected 5 times over the past 6 years. I don’t take being selected for granted because there are so many talented speakers vying for a spot. This year there were 964 talks proposed which appears to be a new record.
    
  &lt;br /&gt;&lt;br /&gt;Unfortunately, I wasn’t selected to give a talk this year but an opportunity arose allowing me to go as an attendee. To save money, I decided to drive up which gave me the opportunity to visit with my dad for a couple days before the conference. My dad lives in Toronto which is about five and a half hours from Montreal. That’s not bad considering I live in Moncton. A trip to see him from Moncton is usually between a 15 and 17-hour drive depending on traffic and if you stop for a break.
  
  &lt;br /&gt;&lt;br /&gt;In the lead-up to the conference, I was digging into Slack so I decided to turn some of that information into a talk just in case another talk is needed. At the very least, I figured I could present it to a local developer user group.
  
  &lt;br /&gt;&lt;br /&gt;The conference kicked off on Wednesday, February 25th and the number of attendees this year seemed higher. After Covid, people were a reluctant to start attending in-person conferences again so it’s nice to see ConFoo bouncing back.
  
  &lt;br /&gt;&lt;br /&gt;This was my first time being at ConFoo as an attendee and that made it a bit different. I was still able to reconnect with some speakers that I hadn’t seen since last year but it was less stressful because I didn’t have my next talk in the back of my mind. 
  
  &lt;br /&gt;&lt;br /&gt;As it would turn out, word came on day 2 of the conference that one of the speakers couldn’t make it because he just became a dad. The organizer asked the speakers if someone would like to replace him so I spoke to the organizer to see if he’d want me to give the replacement talk and he gave me the ok. 
    
  &lt;br /&gt;&lt;br /&gt;I spent the evening rehearsing in my hotel room and then, on Friday, I gave my Slack talk. In return for giving the talk, I received one of this year’s fancy speaker badges as shown in the following image:
  
  &lt;br /&gt;  
  &lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUqDQYMI2jcrt8YZbkBfslUgtxi3F5UuBgOpw0HyoXjumn3itvafKYNrvR48On2MPMBdTfO5W30YpscGe9tM9Ts0vPg372XbZX-faZc5tRO5R8dNQ-wk4os7rB4tfNuQxvrvEWOKsZHqu3AuzIRGz3UIp-752reO0MbLXHCQ4EsmWmoQ7PXDdJAoJfbeo/s3300/attendee_speaker_badges.jpg&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; height=&quot;400&quot; data-original-height=&quot;3300&quot; data-original-width=&quot;2686&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUqDQYMI2jcrt8YZbkBfslUgtxi3F5UuBgOpw0HyoXjumn3itvafKYNrvR48On2MPMBdTfO5W30YpscGe9tM9Ts0vPg372XbZX-faZc5tRO5R8dNQ-wk4os7rB4tfNuQxvrvEWOKsZHqu3AuzIRGz3UIp-752reO0MbLXHCQ4EsmWmoQ7PXDdJAoJfbeo/s400/attendee_speaker_badges.jpg&quot; alt=&quot;The Confoo attendee and speaker badges&quot;/&gt;
    &lt;/a&gt;
  &lt;/div&gt;
  
  As always, it was a really good conference! I learned a lot and got to meet some new people.
  
  &lt;br /&gt;&lt;br /&gt;I’ve already started work on talk ideas for ConFoo 2027 which will be the 25th anniversary of the conference. If you have the opportunity to attend, I highly recommend it. Not just because of the high-quality talks but also because of the networking opportunities with attendees and speakers.
  
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/726727916452210516/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2026/03/confoo-2026-in-review.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/726727916452210516'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/726727916452210516'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2026/03/confoo-2026-in-review.html' title='ConFoo 2026 in Review'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUqDQYMI2jcrt8YZbkBfslUgtxi3F5UuBgOpw0HyoXjumn3itvafKYNrvR48On2MPMBdTfO5W30YpscGe9tM9Ts0vPg372XbZX-faZc5tRO5R8dNQ-wk4os7rB4tfNuQxvrvEWOKsZHqu3AuzIRGz3UIp-752reO0MbLXHCQ4EsmWmoQ7PXDdJAoJfbeo/s72-c/attendee_speaker_badges.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-1701019721294442678</id><published>2026-01-27T22:30:00.003-04:00</published><updated>2026-01-28T09:24:14.369-04:00</updated><title type='text'>The State of WebAssembly - 2025 and 2026</title><content type='html'>&lt;span style=&quot;display:none;&quot;&gt;The State of WebAssembly – 2025 and 2026. For the sixth year in a row, I&#39;ve had the privilege of writing an article on the state of WebAssembly. In this article, I start off by reviewing the WebAssembly developments during 2025 and then I try to predict what I think will happen in 2026.
  
  &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;395&quot; data-original-width=&quot;1200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaT2KuVHprBGZX1pgWfWAbm_wUrm5OejrD7xBMHQJK8RC4w1k4B3uPXwgZ_xpspZFExqQ8KiQ1xudnq1Z9ys55RumJW-tWtjzV4fg0DURKjVvXUtoPN2ip6awNPzCs_pjviYIoF2Ysm0TBot42luyL4Nditjvda2wWKc0iOsHTF8NbesWkjYHxRleW1qk/s1600/BackgroundImage.png&quot;/&gt;
&lt;/span&gt;

&lt;div style=&quot;color:black;font-family:arial;&quot;&gt;
  For the sixth year in a row, I&#39;ve had the privilege of writing an article on the state of WebAssembly (Wasm). 
  
  &lt;br /&gt;&lt;br /&gt;
  In this article, I start off by reviewing the WebAssembly developments during 2025. Things like Safari rounding out browser support for Exception Handling with exnref and JavaScript String Builtins, the progression of Wasm features including the recently announced WebAssembly 3.0 specification milestone, and what&#39;s happening outside the browser in areas like debugging and WASI. 
  
  &lt;br /&gt;&lt;br /&gt;
  The second part of the article gives some insights into what&#39;s possible for 2026.
  
  &lt;br /&gt;&lt;br /&gt;
  As we saw with WebAssembly 3.0 being announced, developer tooling improvements, Wasm support from most of the top 25 programming languages, and adoption in a wide variety of areas, WebAssembly has matured to the point where it&#39;s not longer an experiment. It&#39;s ready for production.
  
  &lt;br /&gt;&lt;br /&gt;
  The article can be found here: &lt;a href=&quot;https://platform.uno/blog/the-state-of-webassembly-2025-2026/&quot; target=&quot;_blank&quot;&gt;The State of WebAssembly - 2025 and 2026&lt;/a&gt;
    
  &lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
    &lt;a href=&quot;https://platform.uno/blog/the-state-of-webassembly-2025-2026/&quot; target=&quot;_blank&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;400&quot; data-original-height=&quot;395&quot; data-original-width=&quot;1200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaT2KuVHprBGZX1pgWfWAbm_wUrm5OejrD7xBMHQJK8RC4w1k4B3uPXwgZ_xpspZFExqQ8KiQ1xudnq1Z9ys55RumJW-tWtjzV4fg0DURKjVvXUtoPN2ip6awNPzCs_pjviYIoF2Ysm0TBot42luyL4Nditjvda2wWKc0iOsHTF8NbesWkjYHxRleW1qk/s1600/BackgroundImage.png&quot;/&gt;
    &lt;/a&gt;

  &lt;/div&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/1701019721294442678/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2026/01/the-state-of-webassembly-2025-and-2026.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/1701019721294442678'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/1701019721294442678'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2026/01/the-state-of-webassembly-2025-and-2026.html' title='The State of WebAssembly - 2025 and 2026'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaT2KuVHprBGZX1pgWfWAbm_wUrm5OejrD7xBMHQJK8RC4w1k4B3uPXwgZ_xpspZFExqQ8KiQ1xudnq1Z9ys55RumJW-tWtjzV4fg0DURKjVvXUtoPN2ip6awNPzCs_pjviYIoF2Ysm0TBot42luyL4Nditjvda2wWKc0iOsHTF8NbesWkjYHxRleW1qk/s72-c/BackgroundImage.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-4482959030659781508</id><published>2025-03-05T21:31:00.000-04:00</published><updated>2025-03-05T21:31:08.818-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ConFoo"/><category scheme="http://www.blogger.com/atom/ns#" term="ElectricSQL"/><category scheme="http://www.blogger.com/atom/ns#" term="libjxl"/><category scheme="http://www.blogger.com/atom/ns#" term="local-first"/><category scheme="http://www.blogger.com/atom/ns#" term="Montreal"/><category scheme="http://www.blogger.com/atom/ns#" term="PGLite"/><category scheme="http://www.blogger.com/atom/ns#" term="SQLite"/><category scheme="http://www.blogger.com/atom/ns#" term="wasm"/><category scheme="http://www.blogger.com/atom/ns#" term="WebAssembly"/><title type='text'>Presenting at ConFoo 2025</title><content type='html'>&lt;span style=&quot;display:none;&quot;&gt;With 191 presentations chosen out of 895 proposals, being accepted to speak at ConFoo is not something I take for granted. This year, my first talk was &quot;Local-First Web Development With the Help of WebAssembly&quot; where I walked the attendees through how SQLite and PGLite compare and work in the browser. My second demo extended the PGLite database to showcase ElectricSQL&#39;s approach to synchronizing your data with a server in a local-first architecture. My second talk, &quot;Using WebAssembly in a Web Component as a Polyfill&quot;, explored using WebAssembly as a polyfill to extend a feature that might not be in all browsers yet. I showed the attendees how to compile the libjxl library to WebAssembly and I used the module in a web component to decode and render a JPEG XL image.
  
  &lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;350&quot; data-original-height=&quot;2546&quot; data-original-width=&quot;2900&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZKZfm3NoPCOCGzMSvpGBX2-1EnD02zR0tSSgn-MpYceMc01lJM1ettQVRec3nsoeV6Vtg4UCAkM0TaEBK2BFeZrSzKMqH42Xqlqi03QI63g0fLAWEiP80d18PzoAHHnnA76vMsl_xPl70hOAlTHX_5KPbLCd3p_89m3M600AbunUF-zh-mn3ECvQc49E/s400/20250304_195737_cropped.jpg&quot; alt=&quot;A picture of this year&#39;s ConFoo program booklet&quot;/&gt;
&lt;/span&gt;

&lt;div style=&quot;color:black;font-family:arial;&quot;&gt;
  
  &lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZKZfm3NoPCOCGzMSvpGBX2-1EnD02zR0tSSgn-MpYceMc01lJM1ettQVRec3nsoeV6Vtg4UCAkM0TaEBK2BFeZrSzKMqH42Xqlqi03QI63g0fLAWEiP80d18PzoAHHnnA76vMsl_xPl70hOAlTHX_5KPbLCd3p_89m3M600AbunUF-zh-mn3ECvQc49E/s2900/20250304_195737_cropped.jpg&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;350&quot; data-original-height=&quot;2546&quot; data-original-width=&quot;2900&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZKZfm3NoPCOCGzMSvpGBX2-1EnD02zR0tSSgn-MpYceMc01lJM1ettQVRec3nsoeV6Vtg4UCAkM0TaEBK2BFeZrSzKMqH42Xqlqi03QI63g0fLAWEiP80d18PzoAHHnnA76vMsl_xPl70hOAlTHX_5KPbLCd3p_89m3M600AbunUF-zh-mn3ECvQc49E/s400/20250304_195737_cropped.jpg&quot; alt=&quot;A picture of this year&#39;s ConFoo program booklet&quot;/&gt;
    &lt;/a&gt;
  &lt;/div&gt;
  
  With 191 presentations chosen out of 895 proposals, being accepted to speak at ConFoo is not something I take for granted. I invest a lot of time into getting the code ready for the demos, preparing slides, and practicing to ensure each talk goes as smoothly as possible while providing the latest cutting-edge information to attendees.

  &lt;br /&gt;&lt;br /&gt;This year, my first talk was &lt;a href=&quot;https://confoo.ca/en/2025/session/local-first-web-development-with-the-help-of-webassembly&quot; target=&quot;_blank&quot;&gt;&lt;b&gt;&quot;Local-First Web Development With the Help of WebAssembly&quot;&lt;/b&gt;&lt;/a&gt; where I first walked the attendees through how &lt;a href=&quot;https://sql.js.org&quot; target=&quot;_blank&quot;&gt;SQLite&lt;/a&gt; and &lt;a href=&quot;https://pglite.dev/&quot; target=&quot;_blank&quot;&gt;PGLite&lt;/a&gt; compare and work in the browser. These products enable web developers to interact with their data in a familiar way by being able to run SQL statements against the database. My second demo extended the PGLite database to showcase &lt;a href=&quot;https://electric-sql.com/&quot; target=&quot;_blank&quot;&gt;ElectricSQL&lt;/a&gt;&#39;s approach to synchronizing your data with a server in a local-first architecture.

  &lt;br /&gt;&lt;br /&gt;My second talk, &lt;a href=&quot;https://confoo.ca/en/2025/session/using-webassembly-in-a-web-component-as-a-polyfill&quot; target=&quot;_blank&quot;&gt;&lt;b&gt;&quot;Using WebAssembly in a Web Component as a Polyfill&quot;&lt;/b&gt;&lt;/a&gt;, explored using WebAssembly as a polyfill to extend a feature that might not be in all browsers yet. For this talk, I showed the audience how to go about compiling a library to WebAssembly, in this case &lt;a href=&quot;https://github.com/libjxl/libjxl&quot; target=&quot;_blank&quot;&gt;libjxl&lt;/a&gt;, and then making use of the module in a web component to decode and render a JPEG XL image.

  &lt;br /&gt;&lt;br /&gt;Because my second talk was before lunch on the second day of the conference, no more practicing was needed so I was able to take it easy for the rest of the conference. That evening, I decided to do some sightseeing. Not knowing Montreal very well, I did a quick online search and found that some people recommended checking out the Saint-Laurent area so I jumped on the subway and then did some exploring. 
  
  &lt;br /&gt;&lt;br /&gt;The following are some of the pictures I took that evening. The left image is from inside the Lionel-Groulx Metro station, the middle image is the Place des Arts building when looking at it from the Saint-Laurent Metro station, and the right image is from my walk down Saint-Catherine Street near the Place des Arts building.

  &lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0aeZhSS7EaNuKnd4znvD31ohEOvA9Fn8AZdL8Pm6ZKgDeSi_5COvcjTiujr9oFKjxjNTHYugqau8gesxc-R9XYEomLAJCzX72ow6PGm_n4o6aE3tcnxtQyAp-o0piM6yAWep97omMgftaM7GIzQpndE84VQnPvSUefFd1kB5SOiQfJB60bZMmKTNMYWU/s4000/20250227_181658.jpg&quot; style=&quot;margin-right:4px;&quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; height=&quot;240&quot; data-original-height=&quot;4000&quot; data-original-width=&quot;3000&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0aeZhSS7EaNuKnd4znvD31ohEOvA9Fn8AZdL8Pm6ZKgDeSi_5COvcjTiujr9oFKjxjNTHYugqau8gesxc-R9XYEomLAJCzX72ow6PGm_n4o6aE3tcnxtQyAp-o0piM6yAWep97omMgftaM7GIzQpndE84VQnPvSUefFd1kB5SOiQfJB60bZMmKTNMYWU/s320/20250227_181658.jpg&quot; alt=&quot;An image of two subway trains on separate levels at the Lionel-Groulx Metro station&quot;/&gt;
    &lt;/a&gt;
    
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMd2fYqX3cESpvNfvCOFVJejMitnfya-tAMiWtxKiilq-1wIG5sv6ZWSRObMKL87mlSUTm5Mu0gbuiG9dP6VEN8JTqccXaDtE5cZdoIzxzjRXJueIWwhQ3QQ9JMNa-yLdcM1jB73C2mPdFunFPGGAaWh5l-0IrD3Fm39sFrvgqKH_oUp7cF1QDshFv55M/s4000/20250227_182959.jpg&quot; style=&quot;margin-right:4px;&quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; height=&quot;240&quot; data-original-height=&quot;4000&quot; data-original-width=&quot;3000&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMd2fYqX3cESpvNfvCOFVJejMitnfya-tAMiWtxKiilq-1wIG5sv6ZWSRObMKL87mlSUTm5Mu0gbuiG9dP6VEN8JTqccXaDtE5cZdoIzxzjRXJueIWwhQ3QQ9JMNa-yLdcM1jB73C2mPdFunFPGGAaWh5l-0IrD3Fm39sFrvgqKH_oUp7cF1QDshFv55M/s320/20250227_182959.jpg&quot; alt=&quot;an image of the Place des Arts building lit up with spotlights shining from the roof near the Saint-Laurent Metro station&quot;/&gt;
    &lt;/a&gt;
    
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-DJP41QzZMztkwfOIvLeweHKHLeuWN-6hxdPvbveVFbhBoHGToeFMdShzdEUqPgrIHqGfZKK2U1kk9QtJDtCVlY52nBBS_WjzO6nBOAi0FHNFrrU7Yh-CxMFNYBiKIguq0Eo0TTGzYSdKkPO38nBAAYIS9Wt1I8Dcy5FOsRziBxM669gRs_yM4RQUrH0/s4000/20250227_183520.jpg&quot; style=&quot;&quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; height=&quot;240&quot; data-original-height=&quot;4000&quot; data-original-width=&quot;3000&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-DJP41QzZMztkwfOIvLeweHKHLeuWN-6hxdPvbveVFbhBoHGToeFMdShzdEUqPgrIHqGfZKK2U1kk9QtJDtCVlY52nBBS_WjzO6nBOAi0FHNFrrU7Yh-CxMFNYBiKIguq0Eo0TTGzYSdKkPO38nBAAYIS9Wt1I8Dcy5FOsRziBxM669gRs_yM4RQUrH0/s320/20250227_183520.jpg&quot; alt=&quot;a walk down Saint-Catherine Street&quot;/&gt;
    &lt;/a&gt;
  &lt;/div&gt;
      
  
  &lt;br /&gt;In 2022, I met a couple guys at ConFoo who were from Fredericton, a city that’s close to where I live. We stayed in contact and would joke about how we had to go all the way to Montreal to meet fellow developers from near us. 
  
  &lt;br /&gt;&lt;br /&gt;This year, the guy who used to lead the developer user group in my city posted on Facebook that he was at the Montreal Canadiens game. I gave his post a like and thought it was neat that he was in Montreal at the same time as me. Much to my surprise, the next morning when I was grabbing breakfast at ConFoo, I heard my name! It turns out that he was in town for ConFoo and we got to hang out a bit.

  &lt;br /&gt;&lt;br /&gt;At the end of each conference, there&#39;s a time for lighting talks by anyone who would like to present. If you&#39;re ever at ConFoo, I recommend it. This year, one of the volunteers gave a presentation talking about Postgres and he kept flipping to a slide promoting an upcoming conference... in Montreal... in May. It was quite humorous and I was glad he was able to join the group of us later that evening for supper. The conference he was talking about is &lt;a href=&quot;https://2025.pgconf.dev/&quot; target=&quot;_blank&quot;&gt;PGConf&lt;/a&gt; if you&#39;re interested.

  &lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
    &lt;a href=&quot;https://2025.pgconf.dev/&quot; target=&quot;_blank&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;320&quot; data-original-height=&quot;371&quot; data-original-width=&quot;704&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHgJVF_3kebV5YNqT-pUVzls6qQApXfIKspogdoX_RWAfhUU7JpUWFrTwOaKCUYOMiLPaB6NEpIE79mPkISn6c9PQSltzzVho6LDDmUQ-yrYCeNzZuJxbFMBVmcoXSRJSiIDT7NTcd8J1pn5apBTLUqjZ3q88D6ba4c4qUOAaz-ynw4ArC2cOXQgXsxGs/s320/PGConf.png&quot;/&gt;
    &lt;/a&gt;
  &lt;/div&gt;
  
  I mentioned to my friends that speaking, especially at ConFoo, is a high. Weeks of work go into preparing and practicing my talks. Then, as the time draws near to give the talk, the nerves hit. Thankfully, the internet stays up, the demos work, and all the practicing pays off as the talk goes well. As soon as the first talk is over, the mind shifts to the next talk I need to give and I practice more to ensure I&#39;m as ready as possible for the next one. In between the stress and nerves leading up to the talk, and then the relief that the talk went well, there&#39;s the networking and meeting of friends from previous years and the making of new friends. It&#39;s a rollercoaster of emotions but, as soon as it&#39;s over, I can&#39;t wait to do it all over again.

  &lt;br /&gt;&lt;br /&gt;Speaking of wanting to do it all over again, if you have a conference and want someone to speak about WebAssembly, something WebAssembly related, or building a Slack bot, I&#39;m your guy. If you&#39;d like to reach out, you can find me on LinkedIn: &lt;a href=&quot;https://www.linkedin.com/in/gerard-gallant&quot; target=&quot;_blank&quot;&gt;linkedin.com/in/gerard-gallant&lt;/a&gt;

  &lt;br /&gt;&lt;br /&gt;If there&#39;s interest, I&#39;d also like to put together a pre-conference workshop related to WebAssembly at ConFoo next year. If you&#39;d be interested in attending and, if there&#39;s anything in particular that you&#39;d like to learn, please fill out the following form: &lt;a href=&quot;https://forms.gle/GSkU923A1k8fdYCG8&quot; target=&quot;_blank&quot;&gt;https://forms.gle/GSkU923A1k8fdYCG8&lt;/a&gt;
  
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/4482959030659781508/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2025/03/presenting-at-confoo-2025.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/4482959030659781508'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/4482959030659781508'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2025/03/presenting-at-confoo-2025.html' title='Presenting at ConFoo 2025'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZKZfm3NoPCOCGzMSvpGBX2-1EnD02zR0tSSgn-MpYceMc01lJM1ettQVRec3nsoeV6Vtg4UCAkM0TaEBK2BFeZrSzKMqH42Xqlqi03QI63g0fLAWEiP80d18PzoAHHnnA76vMsl_xPl70hOAlTHX_5KPbLCd3p_89m3M600AbunUF-zh-mn3ECvQc49E/s72-c/20250304_195737_cropped.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-1151981537192283488</id><published>2025-01-27T20:03:00.004-04:00</published><updated>2025-01-29T20:24:06.306-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="garbage collection"/><category scheme="http://www.blogger.com/atom/ns#" term="Hyperlight"/><category scheme="http://www.blogger.com/atom/ns#" term="Memory64"/><category scheme="http://www.blogger.com/atom/ns#" term="NIST"/><category scheme="http://www.blogger.com/atom/ns#" term="Uno Platform"/><category scheme="http://www.blogger.com/atom/ns#" term="WASI"/><category scheme="http://www.blogger.com/atom/ns#" term="wasm"/><category scheme="http://www.blogger.com/atom/ns#" term="wasm-gc"/><category scheme="http://www.blogger.com/atom/ns#" term="WebAssembly"/><category scheme="http://www.blogger.com/atom/ns#" term="WebAssembly System Interface"/><title type='text'>The State of WebAssembly - 2024 and 2025</title><content type='html'>&lt;span style=&quot;display:none;&quot;&gt;The State of WebAssembly – 2024 and 2025. For the fifth year in a row, I&#39;ve had the privilege of writing an article on the state of WebAssembly. In this article, I start off by reviewing the WebAssembly developments during 2024 like Safari rounding out browser support for a number of features including Garbage Collection, newly available features like JavaScript String Builtins and Memory64, WASI 0.2 (formerly called Preview 2) being released, Uno Platform performance improvements, Hyperlight being open-sourced, and even a NIST report on how wasm can help improve security. Then I try to predict where I think things will go in 2025.
  
  &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;395&quot; data-original-width=&quot;1200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-vX_hWPKh6EwsZmN0z9HgQwmG3oDXchetMdxaaBq77xw7GVyUik333-_5sb3S_kR_Ds3OtL0IasiWS28eMCixBE6xwGTHQ3U_RilKx7KWBloNZeXWpVHCHtUHn_4uepiAErvn48Zq82InH53srNnlQrW2BIj6fpdAisdOnnZmi_0p5n1XGp-KCB2V12c/s1600/BackgroundImage.png&quot;/&gt;
&lt;/span&gt;

&lt;div style=&quot;color:black;font-family:arial;&quot;&gt;
  For the fifth year in a row, I&#39;ve had the privilege of writing an article on the state of WebAssembly. 
  
  &lt;br /&gt;&lt;br /&gt;
  In this article, I start off by reviewing the WebAssembly developments during 2024 like Safari rounding out browser support for a number of features including Garbage Collection, newly available features like JavaScript String Builtins and Memory64, WASI 0.2 (formerly called Preview 2) being released, Uno Platform performance improvements, Hyperlight being open-sourced, and even a NIST report on how wasm can help improve security. Then I try to predict where I think things will go in 2025.
  
  &lt;br /&gt;&lt;br /&gt;
  Every year we see many WebAssembly-related improvements and 2024 was no exception. It&#39;s easy to get caught up waiting on the next amazing feature but the reality is that wasm and WASI are already very capable as we can see from the increased adoption.
  
  
  &lt;br /&gt;&lt;br /&gt;
  The article can be found here: &lt;a href=&quot;https://platform.uno/blog/state-of-webassembly-2024-2025/&quot; target=&quot;_blank&quot;&gt;The State of WebAssembly - 2024 and 2025&lt;/a&gt;
    
  &lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
    &lt;a href=&quot;https://platform.uno/blog/state-of-webassembly-2024-2025/&quot; target=&quot;_blank&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;400&quot; data-original-height=&quot;395&quot; data-original-width=&quot;1200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-vX_hWPKh6EwsZmN0z9HgQwmG3oDXchetMdxaaBq77xw7GVyUik333-_5sb3S_kR_Ds3OtL0IasiWS28eMCixBE6xwGTHQ3U_RilKx7KWBloNZeXWpVHCHtUHn_4uepiAErvn48Zq82InH53srNnlQrW2BIj6fpdAisdOnnZmi_0p5n1XGp-KCB2V12c/s1600/BackgroundImage.png&quot;/&gt;
    &lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/1151981537192283488/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2025/01/the-state-of-webassembly-2024-and-2025.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/1151981537192283488'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/1151981537192283488'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2025/01/the-state-of-webassembly-2024-and-2025.html' title='The State of WebAssembly - 2024 and 2025'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-vX_hWPKh6EwsZmN0z9HgQwmG3oDXchetMdxaaBq77xw7GVyUik333-_5sb3S_kR_Ds3OtL0IasiWS28eMCixBE6xwGTHQ3U_RilKx7KWBloNZeXWpVHCHtUHn_4uepiAErvn48Zq82InH53srNnlQrW2BIj6fpdAisdOnnZmi_0p5n1XGp-KCB2V12c/s72-c/BackgroundImage.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-6729732478044418503</id><published>2024-01-29T12:49:00.001-04:00</published><updated>2024-01-29T12:49:41.626-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="achieve"/><category scheme="http://www.blogger.com/atom/ns#" term="goal"/><category scheme="http://www.blogger.com/atom/ns#" term="habit"/><category scheme="http://www.blogger.com/atom/ns#" term="resolution"/><category scheme="http://www.blogger.com/atom/ns#" term="tips"/><title type='text'>7 Tips for Achieving Your Goals</title><content type='html'>&lt;span style=&quot;display:none;&quot;&gt;January is typically seen by a lot of people as a chance for a new beginning. It’s unfortunate that a lot of people fail to achieve their resolutions and quickly fall back into their old habits. Of those who achieve their goals, are there things they do that can help the rest of us out?
  
  &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;984&quot; data-original-width=&quot;1200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBd1g4XoBqnnkva_jVcVKXW3pckpr82AJ-0E7h7wosDHVvGxMdrOANyI8C_1T9gOIX5GT-3drQ51a_XsRf-k6WDz9s-fet5sEzqewn9UjkMQLB91v4pqd6t1D2mz-9RjkjGAYWuoe24AaB_PX9Lu7wL53OsROPcukm_N68whOL4KVB40h4sB4QfNtbT_c/s1600/night-view-of-arc-de-triomphe_Cropped_publicDomain.jpg&quot;/&gt;
&lt;/span&gt;

&lt;div style=&quot;color:black;font-family:arial;&quot;&gt;
  January is often seen as a chance for a new beginning. It’s a new year so we hit the gym hoping to get that beach body we’ve always wanted. Perhaps we decide to take some courses to advance our career. Maybe the resolution is living a healthier lifestyle or dropping something that we see as a bad habit.

  &lt;br /&gt;&lt;br /&gt;
  As I started writing this article, I remembered a goal I set for myself a few years ago where I wanted to take part in the Paris Marathon. I posted the goal on my office wall with a picture of the &lt;a href=&quot;https://fr.wikipedia.org/wiki/Arc_de_triomphe_de_l%27%C3%89toile&quot; target=&quot;_blank&quot;&gt;Arc de triomphe de l’Étoile&lt;/a&gt; in Paris and I was doing a lot of running to get ready for it. Unfortunately, I lost focus and stopped practicing. 
    
  &lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
    &lt;a href=&quot;https://www.dovico.com/blog/2024/01/29/7-tips-for-achieving-your-goals/&quot; target=&quot;_blank&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;400&quot; data-original-height=&quot;984&quot; data-original-width=&quot;1200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBd1g4XoBqnnkva_jVcVKXW3pckpr82AJ-0E7h7wosDHVvGxMdrOANyI8C_1T9gOIX5GT-3drQ51a_XsRf-k6WDz9s-fet5sEzqewn9UjkMQLB91v4pqd6t1D2mz-9RjkjGAYWuoe24AaB_PX9Lu7wL53OsROPcukm_N68whOL4KVB40h4sB4QfNtbT_c/s1600/night-view-of-arc-de-triomphe_Cropped_publicDomain.jpg&quot;/&gt;
      &lt;br /&gt;The Arc de triomphe de l’Étoile in Paris  
    &lt;/a&gt;
  &lt;/div&gt;

  Whatever the goal, it’s unfortunate that a lot of people fail to achieve their resolutions and quickly fall back into their old habits.
    
  &lt;br /&gt;&lt;br /&gt;
  Of those who achieve their goals, are there things they do that can help the rest of us out?
  
  &lt;br /&gt;&lt;br /&gt;
   In the following article, I present you with &lt;b&gt;&lt;a href=&quot;https://www.dovico.com/blog/2024/01/29/7-tips-for-achieving-your-goals/&quot; target=&quot;_blank&quot;&gt;7 tips to help you realize your goals&lt;/a&gt;&lt;/b&gt;.
  &lt;br /&gt;&lt;br /&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/6729732478044418503/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2024/01/7-tips-for-achieving-your-goals.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/6729732478044418503'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/6729732478044418503'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2024/01/7-tips-for-achieving-your-goals.html' title='7 Tips for Achieving Your Goals'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBd1g4XoBqnnkva_jVcVKXW3pckpr82AJ-0E7h7wosDHVvGxMdrOANyI8C_1T9gOIX5GT-3drQ51a_XsRf-k6WDz9s-fet5sEzqewn9UjkMQLB91v4pqd6t1D2mz-9RjkjGAYWuoe24AaB_PX9Lu7wL53OsROPcukm_N68whOL4KVB40h4sB4QfNtbT_c/s72-c/night-view-of-arc-de-triomphe_Cropped_publicDomain.jpg" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>Moncton, NB, Canada</georss:featurename><georss:point>46.0878165 -64.7782313</georss:point><georss:box>20.516483199143476 -99.9344813 71.659149800856525 -29.6219813</georss:box></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-2653760977719614517</id><published>2024-01-16T22:58:00.002-04:00</published><updated>2024-01-22T14:37:31.564-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term=".NET"/><category scheme="http://www.blogger.com/atom/ns#" term="component model"/><category scheme="http://www.blogger.com/atom/ns#" term="garbage collection"/><category scheme="http://www.blogger.com/atom/ns#" term="SIMD"/><category scheme="http://www.blogger.com/atom/ns#" term="tail calls"/><category scheme="http://www.blogger.com/atom/ns#" term="WASI"/><category scheme="http://www.blogger.com/atom/ns#" term="wasm"/><category scheme="http://www.blogger.com/atom/ns#" term="wasm-gc"/><category scheme="http://www.blogger.com/atom/ns#" term="WebAssembly"/><category scheme="http://www.blogger.com/atom/ns#" term="WebAssembly System Interface"/><title type='text'>The State of WebAssembly: 2023-2024</title><content type='html'>&lt;span style=&quot;display:none;&quot;&gt;The State of WebAssembly – 2023 and 2024. For the fourth year in a row, I&#39;ve had the privilege of writing an article on the state of WebAssembly. In this article, I start off by reviewing the WebAssembly developments during 2023 around Garbage Collection, Tail Calls, fixed-width SIMD, multiple memories, improvements in .NET, and work happening with the WebAssembly System Interface (WASI) and the Component Model. Then I try to predict where I think things will go in 2024.
  
  &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;395&quot; data-original-width=&quot;1200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmo895SP1VdjzDL1J7JLtOCkIoD-g5e9WHlIQ7nRcort7hDgiHUSBNxkb1PvePkQDTnnv8nawUXZQISng4oQ_qeVCUH_jeEoA4O3i4H1-W410yiHjLCJ2wYjVv1pLdlmV0hL2cCcQtIXMrwP9DBsJfBq-qKZEwieiUenM0MXYDk1YRcaB3ht31Cm65rYU/s1600/BackgroundImage.png&quot;/&gt;
&lt;/span&gt;

&lt;div style=&quot;color:black;font-family:arial;&quot;&gt;
  For the fourth year in a row, I&#39;ve had the privilege of writing an article on the state of WebAssembly. 
  
  &lt;br /&gt;&lt;br /&gt;
  In this article, I start off by reviewing the WebAssembly developments during 2023 around Garbage Collection, Tail Calls, fixed-width SIMD, multiple memories, improvements in .NET, and work happening with the WebAssembly System Interface (WASI) and the Component Model. Then I try to predict where I think things will go in 2024.
  
  &lt;br /&gt;&lt;br /&gt;
  A lot happened last year and 2024 is already shaping up to be an exciting year! It feels like WebAssembly&#39;s use is about to take off both as normal WebAssembly modules and as WebAssembly components with WASI.
  
  
  &lt;br /&gt;&lt;br /&gt;
  The article can be found here: &lt;a href=&quot;https://platform.uno/blog/state-of-webassembly-2023-2024/&quot; target=&quot;_blank&quot;&gt;The State of WebAssembly: 2023-2024&lt;/a&gt;
    
  &lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
    &lt;a href=&quot;https://platform.uno/blog/state-of-webassembly-2023-2024/&quot; target=&quot;_blank&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;400&quot; data-original-height=&quot;395&quot; data-original-width=&quot;1200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmo895SP1VdjzDL1J7JLtOCkIoD-g5e9WHlIQ7nRcort7hDgiHUSBNxkb1PvePkQDTnnv8nawUXZQISng4oQ_qeVCUH_jeEoA4O3i4H1-W410yiHjLCJ2wYjVv1pLdlmV0hL2cCcQtIXMrwP9DBsJfBq-qKZEwieiUenM0MXYDk1YRcaB3ht31Cm65rYU/s1600/BackgroundImage.png&quot;/&gt;
    &lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/2653760977719614517/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2024/01/the-state-of-webassembly-2023-2024.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/2653760977719614517'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/2653760977719614517'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2024/01/the-state-of-webassembly-2023-2024.html' title='The State of WebAssembly: 2023-2024'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmo895SP1VdjzDL1J7JLtOCkIoD-g5e9WHlIQ7nRcort7hDgiHUSBNxkb1PvePkQDTnnv8nawUXZQISng4oQ_qeVCUH_jeEoA4O3i4H1-W410yiHjLCJ2wYjVv1pLdlmV0hL2cCcQtIXMrwP9DBsJfBq-qKZEwieiUenM0MXYDk1YRcaB3ht31Cm65rYU/s72-c/BackgroundImage.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-1534719631916877633</id><published>2023-12-19T11:30:00.036-04:00</published><updated>2024-01-18T13:11:07.820-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="2023"/><category scheme="http://www.blogger.com/atom/ns#" term="2024"/><category scheme="http://www.blogger.com/atom/ns#" term="Dovico"/><category scheme="http://www.blogger.com/atom/ns#" term="Dovico Timesheet"/><category scheme="http://www.blogger.com/atom/ns#" term="plans"/><category scheme="http://www.blogger.com/atom/ns#" term="review"/><title type='text'> Dovico Timesheet: Year in Review and Plans for 2024</title><content type='html'>&lt;span style=&quot;display:none;&quot;&gt;As the current calendar year draws to a close and a new one is about to begin, this is often a good time to take a moment to reflect on the past and plan for the future. With that in mind, we wrote an article to review what happened with Dovico Timesheet over the past year as well as our plans for 2024.
  
  &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;642&quot; data-original-width=&quot;1200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnNgAr0IUeg7FDUsJT4Q9B2LF-YuX0mjo-bdxJA6JJesYzivuCcbhnDrx8Iwt7LFWiiz3yj-VBsacQ0RZ4UnEGGX9MCcd0e1Ym85qQwe8Wr-q3GI2Aj7PED4QD5MsnTurTCh5xim-h72CO76ms7joT_OuWvQf8zNrF8IDCkRy0Ppnlt9iknkyXt3ma1qU/s1600/YearEnd%20-%20ArticleCover.png&quot;/&gt;
&lt;/span&gt;

&lt;div style=&quot;color:black;font-family:arial;&quot;&gt;
  As the current calendar year draws to a close and a new one is about to begin, this is often a good time to take a moment to reflect on the past and plan for the future.
  
  &lt;br /&gt;&lt;br /&gt;
  With that in mind, we wrote the following article to review what happened with Dovico Timesheet over the past year as well as our plans for 2024: &lt;a href=&quot;https://www.dovico.com/blog/2023/12/19/dovico-timesheet-year-in-review-and-plans-for-2024/&quot; target=&quot;_blank&quot;&gt;Dovico Timesheet: Year in Review and Plans for 2024&lt;/a&gt;
    
  &lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
    &lt;a href=&quot;https://www.dovico.com/blog/2023/12/19/dovico-timesheet-year-in-review-and-plans-for-2024/&quot; target=&quot;_blank&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;400&quot; data-original-height=&quot;642&quot; data-original-width=&quot;734&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiynYLvqmtGrAufWCQniFrCoqhEsC46d3aAivN8CULrI5DJGJU9m-j5yCwtuVBqlFrO5EtBa7ICd7C6IHuJz54Pg0v5BmW2TjLcpA0WQ7BsqkpnbpaVmnhoArej2AXlzr_8PfaHqYksdwiNzdoKg5i3FTUjUtYp5qIn3bBsioXU-eTaStfuLh3bM-0bDzA/s400/Year%20end%20article%20image.jpg&quot;/&gt;
    &lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/1534719631916877633/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2023/12/dovico-timesheet-year-in-review-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/1534719631916877633'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/1534719631916877633'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2023/12/dovico-timesheet-year-in-review-and.html' title=' Dovico Timesheet: Year in Review and Plans for 2024'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnNgAr0IUeg7FDUsJT4Q9B2LF-YuX0mjo-bdxJA6JJesYzivuCcbhnDrx8Iwt7LFWiiz3yj-VBsacQ0RZ4UnEGGX9MCcd0e1Ym85qQwe8Wr-q3GI2Aj7PED4QD5MsnTurTCh5xim-h72CO76ms7joT_OuWvQf8zNrF8IDCkRy0Ppnlt9iknkyXt3ma1qU/s72-c/YearEnd%20-%20ArticleCover.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-7260759800877770342</id><published>2023-11-22T15:30:00.011-04:00</published><updated>2024-01-18T12:51:05.025-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="assign"/><category scheme="http://www.blogger.com/atom/ns#" term="Dovico"/><category scheme="http://www.blogger.com/atom/ns#" term="Dovico Timesheet"/><category scheme="http://www.blogger.com/atom/ns#" term="employees"/><category scheme="http://www.blogger.com/atom/ns#" term="projects"/><category scheme="http://www.blogger.com/atom/ns#" term="Quick Assign"/><category scheme="http://www.blogger.com/atom/ns#" term="task templates"/><category scheme="http://www.blogger.com/atom/ns#" term="unassign"/><title type='text'>Dovico Timesheet - Quick Assign</title><content type='html'>&lt;span style=&quot;display:none;&quot;&gt;We&#39;re pleased to announce that a new Quick Assign view has been created for Dovico Timesheet that allows you to assign or unassign employees across projects quickly!
  
  &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;579&quot; data-original-width=&quot;1200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj66r208l0lmIqDKvParvKYPGlFhqiDmuoGNhpwdoWEoOVwxzvsKndEcoAt5XfyjTeYzrgw7rmL7f57YS6azICHzbWNwszXzMejJ-A9-OWk9dRSOd1hL9Hy0rEFjWMnflDnnQ__ICqGkgRCZirYet1P-71iJnsk1PAXgHo4xHArGC-AJwQJLjHtRASvH1g/s1600/QuickAssign%20-%20ArticleCover.png&quot;/&gt;
&lt;/span&gt;

&lt;div style=&quot;color:black;font-family:arial;&quot;&gt;
  We&#39;re pleased to announce that a new Quick Assign view has been created for &lt;a href=&quot;https://www.dovico.com/&quot; target=&quot;_blank&quot;&gt;Dovico Timesheet&lt;/a&gt; that allows you to assign or unassign employees across projects quickly!
  
  &lt;br /&gt;&lt;br /&gt;
  The new Quick Assign view is made possible thanks to a feature we released earlier this year, where tasks can now remain linked to a task template.
    
  &lt;br /&gt;&lt;br /&gt;
  The following article walks you through how the new view works: &lt;a href=&quot;https://www.dovico.com/blog/2023/11/22/dovico-timesheet-quick-assign/&quot; target=&quot;_blank&quot;&gt;Dovico Timesheet Quick Assign&lt;/a&gt;
    
  &lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
    &lt;a href=&quot;https://www.dovico.com/blog/2023/11/22/dovico-timesheet-quick-assign/&quot; target=&quot;_blank&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;600&quot; data-original-height=&quot;579&quot; data-original-width=&quot;1200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj66r208l0lmIqDKvParvKYPGlFhqiDmuoGNhpwdoWEoOVwxzvsKndEcoAt5XfyjTeYzrgw7rmL7f57YS6azICHzbWNwszXzMejJ-A9-OWk9dRSOd1hL9Hy0rEFjWMnflDnnQ__ICqGkgRCZirYet1P-71iJnsk1PAXgHo4xHArGC-AJwQJLjHtRASvH1g/s1600/QuickAssign%20-%20ArticleCover.png&quot;/&gt;
    &lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/7260759800877770342/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2023/11/dovico-timesheet-quick-assign.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/7260759800877770342'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/7260759800877770342'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2023/11/dovico-timesheet-quick-assign.html' title='Dovico Timesheet - Quick Assign'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj66r208l0lmIqDKvParvKYPGlFhqiDmuoGNhpwdoWEoOVwxzvsKndEcoAt5XfyjTeYzrgw7rmL7f57YS6azICHzbWNwszXzMejJ-A9-OWk9dRSOd1hL9Hy0rEFjWMnflDnnQ__ICqGkgRCZirYet1P-71iJnsk1PAXgHo4xHArGC-AJwQJLjHtRASvH1g/s72-c/QuickAssign%20-%20ArticleCover.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-6220225527455194464</id><published>2023-05-09T16:00:00.087-03:00</published><updated>2024-01-18T12:52:39.259-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Dovico"/><category scheme="http://www.blogger.com/atom/ns#" term="Dovico Timesheet"/><category scheme="http://www.blogger.com/atom/ns#" term="projects"/><category scheme="http://www.blogger.com/atom/ns#" term="task templates"/><category scheme="http://www.blogger.com/atom/ns#" term="tasks"/><title type='text'>Dovico Timesheet - Allowing tasks to be kept linked to a task template</title><content type='html'>&lt;span style=&quot;display:none;&quot;&gt;We just released a feature for Dovico Timesheet where a task can be kept linked to a task template. This new feature allows you to easily adjust the properties of the task template and have those changes applied to all tasks that are linked to it.  
  
  &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;550&quot; data-original-width=&quot;1200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzYg3Pfua4fGvR0nLY0HXgdLkAFfB5ikwYJc0iUL719d8eG_6GaP32HzszjY49G_ADUElPTyUcqz5C5r2MA2XpCSW1ZP1MhmrduOuQ8wS8zE0ABeuWEO_YK8AWyI-xheyTbQHQf43uqBvP6VbUjfuvtG7cypN0WjErKjJUfu74Ncfk2DgATOFh-wXUZKM/s1600/TaskTemplate%20ArticleCover.png&quot;/&gt;
  
&lt;/span&gt;

&lt;div style=&quot;color:black;font-family:arial;&quot;&gt;
  Today, I&#39;m pleased to announce that the company that I work for, &lt;a href=&quot;https://www.dovico.com/&quot; target=&quot;_blank&quot;&gt;Dovico Software&lt;/a&gt;, just released a feature for our Dovico Timesheet product allowing tasks to be kept linked to a task template.
  
  &lt;br /&gt;&lt;br /&gt;
  Because a project can have multiple tasks created from the same task template, it can be time consuming, tedious, and error prone to manually make adjustments to each task especially if you need to make adjustments across projects.
  
  &lt;br /&gt;&lt;br /&gt;
  This new feature allows you to easily adjust the properties of the task template itself and have those changes applied to all tasks that are linked to it.
    
  &lt;br /&gt;&lt;br /&gt;
  For more information on how this feature works, the following article gives a detailed overview: &lt;a href=&quot;https://www.dovico.com/blog/2023/05/09/dovico-timesheet-updates-for-may-2023/&quot; target=&quot;_blank&quot;&gt;The ability to have a task linked to a task template&lt;/a&gt;
    
  &lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
    &lt;a href=&quot;https://www.dovico.com/blog/2023/05/09/dovico-timesheet-updates-for-may-2023/&quot; target=&quot;_blank&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;600&quot; data-original-height=&quot;550&quot; data-original-width=&quot;1200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzYg3Pfua4fGvR0nLY0HXgdLkAFfB5ikwYJc0iUL719d8eG_6GaP32HzszjY49G_ADUElPTyUcqz5C5r2MA2XpCSW1ZP1MhmrduOuQ8wS8zE0ABeuWEO_YK8AWyI-xheyTbQHQf43uqBvP6VbUjfuvtG7cypN0WjErKjJUfu74Ncfk2DgATOFh-wXUZKM/s1600/TaskTemplate%20ArticleCover.png&quot;/&gt;
    &lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/6220225527455194464/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2023/05/dovico-timesheet-allowing-tasks-to-be.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/6220225527455194464'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/6220225527455194464'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2023/05/dovico-timesheet-allowing-tasks-to-be.html' title='Dovico Timesheet - Allowing tasks to be kept linked to a task template'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzYg3Pfua4fGvR0nLY0HXgdLkAFfB5ikwYJc0iUL719d8eG_6GaP32HzszjY49G_ADUElPTyUcqz5C5r2MA2XpCSW1ZP1MhmrduOuQ8wS8zE0ABeuWEO_YK8AWyI-xheyTbQHQf43uqBvP6VbUjfuvtG7cypN0WjErKjJUfu74Ncfk2DgATOFh-wXUZKM/s72-c/TaskTemplate%20ArticleCover.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-8311199978188301393</id><published>2023-03-29T12:30:00.005-03:00</published><updated>2024-05-01T20:35:58.057-03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term=".NET"/><category scheme="http://www.blogger.com/atom/ns#" term="ahead-of-time compile"/><category scheme="http://www.blogger.com/atom/ns#" term="AOT"/><category scheme="http://www.blogger.com/atom/ns#" term="Blazor"/><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><category scheme="http://www.blogger.com/atom/ns#" term="Safari"/><category scheme="http://www.blogger.com/atom/ns#" term="SIMD"/><category scheme="http://www.blogger.com/atom/ns#" term="Uno Platform"/><category scheme="http://www.blogger.com/atom/ns#" term="wasm"/><category scheme="http://www.blogger.com/atom/ns#" term="WebAssembly"/><title type='text'>Safari 16.4 and WebAssembly Fixed-Width SIMD from C#</title><content type='html'>&lt;div style=&quot;color:black;font-family:arial;padding-bottom:20px;&quot;&gt;
  This week, Safari 16.4 was released and with it came support for WebAssembly&#39;s fixed-width SIMD feature! With this update, all modern browsers now support this feature.
  
  &lt;br /&gt;&lt;br /&gt;
  As shown in the following image, WebAssembly fixed-width SIMD allows code to take advantage of hardware instructions on the device to speed up certain computations by running them in parallel. 

  &lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiChYPQATCnlD6cV4hcdpRPGTcL_bsTojJUHQAgflXpp0R35NKKWD1adyFjKfLbml0fl9bXJ1bNPA4JvS7XuoQ8W0k-s5jOjliuuC2d5SeaS0pRw0pPxgqU7_EA8mFMkMi8L-UoqhMP5IBfPBn98GkpLc1EtXsErGdKRsMPa0leoUCEjLfPTFUi6Kpv/s1600/1.%20SISD%20vs%20SIMD.png&quot; style=&quot;display: block; padding: 1em 0 0 0; text-align: center;&quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;258&quot; data-original-width=&quot;372&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiChYPQATCnlD6cV4hcdpRPGTcL_bsTojJUHQAgflXpp0R35NKKWD1adyFjKfLbml0fl9bXJ1bNPA4JvS7XuoQ8W0k-s5jOjliuuC2d5SeaS0pRw0pPxgqU7_EA8mFMkMi8L-UoqhMP5IBfPBn98GkpLc1EtXsErGdKRsMPa0leoUCEjLfPTFUi6Kpv/s1600/1.%20SISD%20vs%20SIMD.png&quot;/&gt;
    &lt;/a&gt; 
    &lt;p style=&quot;text-align:center;&quot;&gt;A visual representation comparing data being processed
      						&lt;br /&gt;one element at a time with normal arithmetic &lt;i&gt;(Single
      						&lt;br /&gt;Instruction, Single Data)&lt;/i&gt; versus four at a time, in
      						&lt;br /&gt;this case, with SIMD&lt;/p&gt;
  &lt;/div&gt;
    
  &lt;br /&gt;
  The Uno Platform allows you to write an application that works on multiple systems including in the browser thanks to WebAssembly. The Uno Platform uses .NET and the ability to target WebAssembly fixed-width SIMD was added in .NET 7.0.

  &lt;br /&gt;&lt;br /&gt;
  I wrote the following article that walks you through creating an Uno Platform application and how to work with vectors to leverage SIMD. The article also explains how to compile your application ahead-of-time (AOT) with SIMD support: &lt;a href=&quot;https://platform.uno/blog/safari-16-4-support-for-webassembly-fixed-width-simd-how-to-use-it-with-c/&quot; target=&quot;_blank&quot;&gt;https://platform.uno/blog/safari-16-4-support-for-webassembly-fixed-width-simd-how-to-use-it-with-c/&lt;/a&gt;


  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;
  &lt;div style=&quot;font-weight:bold;&quot;&gt;Leveraging WebAssembly fixed-width SIMD in Blazor WebAssembly&lt;/div&gt;
  
  &lt;br /&gt;
  Because the Uno Platform is using .NET, with a few slight modifications the same code can also be used in Blazor WebAssembly as well.

  &lt;br /&gt;&lt;br /&gt;
  With both platforms, to take advantage of WebAssembly&#39;s fixed-width SIMD support, you need to AOT compile the application but there are a couple differences between how it&#39;s done with the Uno Platform versus Blazor.

  &lt;br /&gt;&lt;br /&gt;
  For Blazor, you&#39;ll need to edit your project file and include the following tags in a PropertyGroup tag:
  &lt;ul&gt;
    &lt;li&gt;&lt;b&gt;&amp;lt;RunAOTCompilation&amp;gt;true&amp;lt;/RunAOTCompilation&amp;gt;&lt;/b&gt;&lt;/li&gt;
    &lt;li&gt;&lt;b&gt;&amp;lt;WasmEnableSIMD&amp;gt;true&amp;lt;/WasmEnableSIMD&amp;gt;&lt;/b&gt;&lt;/li&gt;
  &lt;/ul&gt;

  &lt;br /&gt;
  With the Uno Platform, simply adding Uno&#39;s version of the previous tags is enough to trigger the AOT process depending on if your build configuration matches that of the PropertyGroup you added them to &lt;i&gt;(Release mode for example)&lt;/i&gt;. However, &lt;b&gt;with Blazor, AOT is only triggered when you publish the project&lt;/b&gt;.

  &lt;br /&gt;&lt;br /&gt;
  Also with Blazor, the AOT option on the Publish dialog is on and you can&#39;t uncheck it. With Uno you need to make sure you don&#39;t check it by accident because doing so will cause an error to be thrown that can only be fixed by deleting a tag from the publish profile file.

  &lt;br /&gt;&lt;br /&gt;
  A Blazor WebAssembly version of the code can be found here: &lt;a href=&quot;https://github.com/cggallant/blog_post_code/tree/master/2023-March-BlazorSIMD&quot; target=&quot;_blank&quot;&gt;https://github.com/cggallant/blog_post_code/tree/master/2023-March-BlazorSIMD&lt;/a&gt;
  
   
  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;
  &lt;div style=&quot;font-weight:bold;&quot;&gt;Demos&lt;/div&gt;
  
  &lt;br /&gt;
  If you&#39;d like to see it in action, the following links are demos built form the code in the article. Note that because these are AOT compiled, they are bigger so they may take a few seconds to download and display especially if you&#39;re on an older device or on a slower network.
  &lt;ul&gt;
    &lt;li&gt;Uno Platform: &lt;a href=&quot;https://sda-bytes.com/uno-wasm-simd/index.html&quot; target=&quot;_blank&quot;&gt;https://sda-bytes.com/uno-wasm-simd/index.html&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;Blazor WebAssembly: &lt;a href=&quot;https://sda-bytes.com/blazor-wasm-simd/&quot; target=&quot;_blank&quot;&gt;https://sda-bytes.com/blazor-wasm-simd/&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;

&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/8311199978188301393/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2023/03/safari-164-and-webassembly-fixed-width.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/8311199978188301393'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/8311199978188301393'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2023/03/safari-164-and-webassembly-fixed-width.html' title='Safari 16.4 and WebAssembly Fixed-Width SIMD from C#'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiChYPQATCnlD6cV4hcdpRPGTcL_bsTojJUHQAgflXpp0R35NKKWD1adyFjKfLbml0fl9bXJ1bNPA4JvS7XuoQ8W0k-s5jOjliuuC2d5SeaS0pRw0pPxgqU7_EA8mFMkMi8L-UoqhMP5IBfPBn98GkpLc1EtXsErGdKRsMPa0leoUCEjLfPTFUi6Kpv/s72-c/1.%20SISD%20vs%20SIMD.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-5273789883819142303</id><published>2023-02-28T22:51:00.000-04:00</published><updated>2023-03-29T20:09:38.981-03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ConFoo"/><category scheme="http://www.blogger.com/atom/ns#" term="Docker"/><category scheme="http://www.blogger.com/atom/ns#" term="Dovico"/><category scheme="http://www.blogger.com/atom/ns#" term="Hotel Bonaventure"/><category scheme="http://www.blogger.com/atom/ns#" term="Montreal"/><category scheme="http://www.blogger.com/atom/ns#" term="Slack"/><category scheme="http://www.blogger.com/atom/ns#" term="Uno Platform"/><title type='text'>ConFoo 2023 in Review</title><content type='html'>&lt;span style=&quot;display:none;&quot;&gt;My experience at ConFoo 2023
  &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;916&quot; data-original-width=&quot;1200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUF8BbMU_jYLoVRIJX2y0hSYq7KFZ8krsTnceXZIOyOOyVCDK32jd8y5Om8uDCMuQ-IJDsJFv3kdWcni6ED0XkdJd-FPWtzJeSFkwvo_LuMRVITgpUbRQpOHhZQ5HfezrsphKby8XLoFRWYeoPy5mmmyC5AuPSWK-ca2s6LAIurqgSOOLLxZjsT2-H/s1600/booklet_cropped_1200px_wide.jpg&quot;/&gt;
&lt;/span&gt;

&lt;div style=&quot;color:black;font-family:arial;&quot;&gt;
  &lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWUPAWHN4kqjdzvC966n5yeQFzwdhPqSs9p9pDetWPkZRN8ham76FDS306pZrvtnACfwGhx0JwPpheEwxGjbnvqsTj_KxjtPgB18275Vm8MWswp50HvfsLRln4UJnVN1-BSNKOIlvNYzEn6JBAXpPMmONZPxiiZeraVGpjbSaFFD9yqY5TiGyKgGsq/s3000/booklet_cropped.jpg&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;380&quot; data-original-height=&quot;2289&quot; data-original-width=&quot;3000&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWUPAWHN4kqjdzvC966n5yeQFzwdhPqSs9p9pDetWPkZRN8ham76FDS306pZrvtnACfwGhx0JwPpheEwxGjbnvqsTj_KxjtPgB18275Vm8MWswp50HvfsLRln4UJnVN1-BSNKOIlvNYzEn6JBAXpPMmONZPxiiZeraVGpjbSaFFD9yqY5TiGyKgGsq/s380/booklet_cropped.jpg&quot;/&gt;
    &lt;/a&gt;
  &lt;/div&gt;
  
  
  On Tuesday, February 21st, I started my journey to Montreal to speak at the ConFoo developer conference for my third time.

  &lt;br /&gt;&lt;br /&gt;
  Usually, when I travel to the big cities, I try to use public transportation to get to and from the airport. The city I live in isn&#39;t very big but I decided to give our transportation system a try anyway.
  
  &lt;br /&gt;&lt;br /&gt;
  Unfortunately, for me to get to the airport using this approach, I needed to take two city buses and then a coach bus. It&#39;s doable but it takes time and the coach bus only goes to the airport twice a day so the timing didn&#39;t allow me to use this approach when I returned. 

  &lt;br /&gt;&lt;br /&gt;
  One highlight of my trip to the airport, however, was a pheasant that graced me with his presence.
  
  &lt;br /&gt;
  &lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwlKbUTugYipAKz174e24pKumQAGLU06gbspEeLTai9snXHUQNh8CUb3-4iUWWlVgjf2H-bjtUS-Q6812p8vjUMP9xwtqHBnH18MNnMGLnjSMvQoAGWJDzQK_s9pBXyUHzpHAUp0rCQZmgHL7RPU12mL1ZwVEgUVnCLj0YfPZxZfcZVOYAZW8JNkqX/s1585/pheasant_cropped.jpg&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;250&quot; data-original-height=&quot;1400&quot; data-original-width=&quot;1585&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwlKbUTugYipAKz174e24pKumQAGLU06gbspEeLTai9snXHUQNh8CUb3-4iUWWlVgjf2H-bjtUS-Q6812p8vjUMP9xwtqHBnH18MNnMGLnjSMvQoAGWJDzQK_s9pBXyUHzpHAUp0rCQZmgHL7RPU12mL1ZwVEgUVnCLj0YfPZxZfcZVOYAZW8JNkqX/s250/pheasant_cropped.jpg&quot;/&gt;
    &lt;/a&gt;
  &lt;/div&gt;
  
  The conference was held at the Hotel Bonaventure where we were greeted by over 700 attendees and over 150 sessions in 3 days! Being in Montreal, it was fitting that some of the sessions were also available in French.

  &lt;br /&gt;&lt;br /&gt;
  &lt;div class=&quot;separator&quot; style=&quot;clear: both;text-align: center;&quot;&gt;
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYFcNjQwZwPZnQmdHMnBlXPzk00i1lCAiwbvG77hNIxoJQYJZdkD3yZ-7-MxrWk9fmXN3IWuDgME81fMp5Gwc8VdSZZg2NA5DYQN1ihbfKUomk5eHucw7zXyjjgOOAJ6cFxNnvc6ZqeNgk5eL6PADGj_jEKUom3wK1OlvhYKsB3CNEa3hewv6AxMKR/s4000/20230221_205801.jpg&quot; style=&quot;margin-right:4px;&quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; height=&quot;350&quot; data-original-height=&quot;4000&quot; data-original-width=&quot;3000&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYFcNjQwZwPZnQmdHMnBlXPzk00i1lCAiwbvG77hNIxoJQYJZdkD3yZ-7-MxrWk9fmXN3IWuDgME81fMp5Gwc8VdSZZg2NA5DYQN1ihbfKUomk5eHucw7zXyjjgOOAJ6cFxNnvc6ZqeNgk5eL6PADGj_jEKUom3wK1OlvhYKsB3CNEa3hewv6AxMKR/s350/20230221_205801.jpg&quot;/&gt;
    &lt;/a&gt;
    
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8hol0iWh4GLOVF-ZM0lP-GBzO8jXokpYY5jWJHVXD0i8dIQSGTg_SXI-n4VkvdAodaiJUMMB05-RAxE2txyyXz8w4V64RQbqBBHbtI-RU2biykKvRNoFA5nueBOucpB7tZYYckSPPZEFY_GHZTRcpsZLhGw3hAxel8iPx1YVDwzeCRPR8unKG_TkR/s4000/20230222_085306.jpg&quot; style=&quot;margin-left:4px;&quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; height=&quot;350&quot; data-original-height=&quot;4000&quot; data-original-width=&quot;3000&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8hol0iWh4GLOVF-ZM0lP-GBzO8jXokpYY5jWJHVXD0i8dIQSGTg_SXI-n4VkvdAodaiJUMMB05-RAxE2txyyXz8w4V64RQbqBBHbtI-RU2biykKvRNoFA5nueBOucpB7tZYYckSPPZEFY_GHZTRcpsZLhGw3hAxel8iPx1YVDwzeCRPR8unKG_TkR/s350/20230222_085306.jpg&quot;/&gt;
    &lt;/a&gt;
  &lt;/div&gt;
  
  &lt;br /&gt;
  The past two times that I spoke at this conference, I only spoke for one session each time. This year, I gave two presentations.

  &lt;br /&gt;&lt;br /&gt;
  My first presentation was given on the second day of the conference. For this session, I talked about how we&#39;re using Docker at Dovico to prototype views, try out different UI frameworks, and help speed up the development of Timesheet.
  &lt;div class=&quot;separator&quot; style=&quot;clear: both;text-align:center;&quot;&gt;
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheVVvWZt6ZSFfjnqh9-Wr3ikhoXPorv4c9pjZxuNdhsIHo8HFBZvfLYKJZE580YNpG7orC-ctmiUEDD3SUK4b05ujXsPGDtHhGGQ4XRZ0DICsVsUt6z5NmkQCbiBlLREeFbe2uWrMiGZdky5H0IQHizyG2tzZKrV82xcSXKtB9tFtZTJeqH77n6AJR/s4991/Talk1.jpg&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;580&quot; data-original-height=&quot;3327&quot; data-original-width=&quot;4991&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheVVvWZt6ZSFfjnqh9-Wr3ikhoXPorv4c9pjZxuNdhsIHo8HFBZvfLYKJZE580YNpG7orC-ctmiUEDD3SUK4b05ujXsPGDtHhGGQ4XRZ0DICsVsUt6z5NmkQCbiBlLREeFbe2uWrMiGZdky5H0IQHizyG2tzZKrV82xcSXKtB9tFtZTJeqH77n6AJR/s580/Talk1.jpg&quot;/&gt;
    &lt;/a&gt;
    &lt;i&gt;(source: &lt;a href=&quot;https://www.flickr.com/photos/confoo/52734935208&quot; target=&quot;_blank&quot;&gt;https://www.flickr.com/photos/confoo/52734935208&lt;/a&gt;)&lt;/i&gt;
  &lt;/div&gt;

  &lt;br /&gt;
  The second presentation that I gave was on the final day of the conference. This time, I walked the audience though creating a Slack bot from start to finish by building a simple hot desk booking system!

  &lt;div class=&quot;separator&quot; style=&quot;clear: both;text-align:center;&quot;&gt;
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrrSx39-uhYVSXrDXviQV1AT34aSuTkgnXEYsGnIjmfJiUnJakrKSjOpPuzCOHtzqxlBc2QsbpFLvhKotL6t_Eu450gzQ9Q3qrJ7jY-rjdzIOjBeqM5dMkQo7hwjtO7ebtG-leQUjezRMmVRKpZcotTNOh-7s-f-KndcYARA83CIoOoqZWi5QUXiXa/s5472/Talk2.jpg&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;580&quot; data-original-height=&quot;3648&quot; data-original-width=&quot;5472&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrrSx39-uhYVSXrDXviQV1AT34aSuTkgnXEYsGnIjmfJiUnJakrKSjOpPuzCOHtzqxlBc2QsbpFLvhKotL6t_Eu450gzQ9Q3qrJ7jY-rjdzIOjBeqM5dMkQo7hwjtO7ebtG-leQUjezRMmVRKpZcotTNOh-7s-f-KndcYARA83CIoOoqZWi5QUXiXa/s580/Talk2.jpg&quot;/&gt;
    &lt;/a&gt;
    &lt;i&gt;(source: &lt;a href=&quot;https://www.flickr.com/photos/confoo/52733940852&quot; target=&quot;_blank&quot;&gt;https://www.flickr.com/photos/confoo/52733940852&lt;/a&gt;)&lt;/i&gt;
  &lt;/div&gt;
  
  &lt;br /&gt;
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;div class=&quot;divIndent&quot;&gt;
      My slides, notes, and links to the code from my talks can be found in the following GitHub repository: &lt;a href=&quot;https://github.com/cggallant/confoo-2023&quot; target=&quot;_blank&quot;&gt;https://github.com/cggallant/confoo-2023&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;br /&gt;
  As my new friend, &lt;a href=&quot;https://twitter.com/ajpinedam&quot; target=&quot;_blank&quot;&gt;Andres Pineda&lt;/a&gt;, so correctly articulated in one of his sessions, there&#39;s more to a developer conference than just taking in the talks. There&#39;s also a networking component to these conferences. When I saw him at the opening party for the speakers, I recognized him but couldn&#39;t figure out where from. Later I realized that I had seen him talk online as part of UnoConf and he&#39;s one of the contributors to the open-source &lt;a href=&quot;https://platform.uno/&quot; target=&quot;_blank&quot;&gt;Uno Platform&lt;/a&gt;. We have something in common because I&#39;ve been privileged to work with Uno and write them several articles for their blog. We were able to connect during the conference.

  &lt;br /&gt;&lt;br /&gt;
  Speaking of the Uno Platform, while I was in Montreal, I had the opportunity to meet &lt;a href=&quot;https://twitter.com/mx2ei1&quot; target=&quot;_blank&quot;&gt;Matthew Mattei&lt;/a&gt; in person for lunch. I work with him when writing articles for their blog. I wasn&#39;t expecting it but I do appreciate that the Uno Platform sent me home with some swag:

  &lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjat3YWmaziCJuF47a7XZF8bx9OjL-KsPHMWYNjQmwrliSUAqJSax8p-lDdH-31QV1lNqdi4CxGLHorE-buDhGRyt6hIzoDN_fH064fkWq6UWHuAmPk5cWeiQ6paQjrTubVzSnKcYI57XuqJIJyOgCsA8QmgpoXJE6jI2YqtFigJ4ckVH6UxgO-cRze/s4000/20230225_172007.jpg&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; height=&quot;320&quot; data-original-height=&quot;4000&quot; data-original-width=&quot;3000&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjat3YWmaziCJuF47a7XZF8bx9OjL-KsPHMWYNjQmwrliSUAqJSax8p-lDdH-31QV1lNqdi4CxGLHorE-buDhGRyt6hIzoDN_fH064fkWq6UWHuAmPk5cWeiQ6paQjrTubVzSnKcYI57XuqJIJyOgCsA8QmgpoXJE6jI2YqtFigJ4ckVH6UxgO-cRze/s320/20230225_172007.jpg&quot;/&gt;
    &lt;/a&gt;
  &lt;/div&gt;
    
  Over the course of the conference, I was fortunate to meet some great people and to take in a number of really good sessions. My only regret is not being able to take in all of the sessions because there are usually 8 other sessions happening at the same time as the session that you&#39;re in.
  
  &lt;br /&gt;&lt;br /&gt;
  On Saturday, February 25th, I made the journey from the hotel back to the Montreal airport. I thought this was a neat idea as I was walking through the airport to my gate:
  
  &lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-wUggwFG_J5DuKp6yuixoCK9CNCYM83h8BXl07TH5CYfmqYqMil3ioxWqJyJGORucEVtVRqS0Z6wJRP19ps6SOCjGfxoQNj1Hl1ohIZ9aCYaowKVXJxZ0orb3zvIbfhyq3GIgSjmYm_L4Vie7JMhcr2D41p8jxGRDzW_tNO8V72VnwkOAUBXCt78F/s2691/Montreal_airport_cropped.jpg&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;320&quot; data-original-height=&quot;2652&quot; data-original-width=&quot;2691&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-wUggwFG_J5DuKp6yuixoCK9CNCYM83h8BXl07TH5CYfmqYqMil3ioxWqJyJGORucEVtVRqS0Z6wJRP19ps6SOCjGfxoQNj1Hl1ohIZ9aCYaowKVXJxZ0orb3zvIbfhyq3GIgSjmYm_L4Vie7JMhcr2D41p8jxGRDzW_tNO8V72VnwkOAUBXCt78F/s320/Montreal_airport_cropped.jpg&quot;/&gt;
    &lt;/a&gt;
  &lt;/div&gt;

  It was a great conference and I already miss it. I hope to be able to attend next year&#39;s conference and I recommend ConFoo to everyone.
  
  &lt;br /&gt;&lt;br /&gt;
  A huge shout out to &lt;a href=&quot;https://twitter.com/ylarrivee&quot; target=&quot;_blank&quot;&gt;Yann Larrivée&lt;/a&gt; for putting on another amazing event!


  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;
  As a side note, if you happen to be putting on a developer conference, or know of one that&#39;s looking for speakers, let me know because I&#39;d like to speak more. I&#39;ve started setting up a &lt;a href=&quot;https://sessionize.com/gerard-gallant&quot; target=&quot;_blank&quot;&gt;Sessionize&lt;/a&gt; profile but you can also reach me on &lt;a href=&quot;https://twitter.com/Gerard_Gallant&quot; target=&quot;_blank&quot;&gt;Twitter&lt;/a&gt; or on &lt;a href=&quot;https://www.linkedin.com/in/gerard-gallant&quot; target=&quot;_blank&quot;&gt;LinkedIn&lt;/a&gt;.
  
  &lt;br /&gt;&lt;br /&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/5273789883819142303/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2023/03/confoo-2023-in-review.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/5273789883819142303'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/5273789883819142303'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2023/03/confoo-2023-in-review.html' title='ConFoo 2023 in Review'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUF8BbMU_jYLoVRIJX2y0hSYq7KFZ8krsTnceXZIOyOOyVCDK32jd8y5Om8uDCMuQ-IJDsJFv3kdWcni6ED0XkdJd-FPWtzJeSFkwvo_LuMRVITgpUbRQpOHhZQ5HfezrsphKby8XLoFRWYeoPy5mmmyC5AuPSWK-ca2s6LAIurqgSOOLLxZjsT2-H/s72-c/booklet_cropped_1200px_wide.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-8204464891152951946</id><published>2023-01-30T19:55:00.002-04:00</published><updated>2023-01-30T19:57:25.051-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript"/><category scheme="http://www.blogger.com/atom/ns#" term="State of WebAssembly"/><category scheme="http://www.blogger.com/atom/ns#" term="wasm"/><category scheme="http://www.blogger.com/atom/ns#" term="WebAssembly"/><title type='text'>The State of WebAssembly - 2022 and 2023</title><content type='html'>&lt;span style=&quot;display:none;&quot;&gt;The State of WebAssembly – 2022 and 2023. For the third year in a row, I&#39;ve had the privilege of writing an article on the state of WebAssembly. In this article, I started by revisiting developments during 2022 to see if any of my predictions came true and if there were any surprises. Then I tried to predict where I think things will go in 2023.
  
  &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;395&quot; data-original-width=&quot;1200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjM0gvX05k0biyyIdByvPh5biXa1pDUo1PO7zCuK91qppm2vADdiLES-sDF9AXUepoCOSwTILe6-7p9paSKZjgrM9gE0tyqMUmbIdt3KjakgYQLQXkx9ITi41ilevvS0zFB04teDxVYLyCUNrzh3_AJf8IZ5AwmWIksH8acvjS6kP8urMs-5CdkLCip/s1600/TSO_WebAssembly_Cover_1200px%20wide.jpg&quot;/&gt;
&lt;/span&gt;

&lt;div style=&quot;color:black;font-family:arial;&quot;&gt;
  For the third year in a row, I&#39;ve had the privilege of writing an article on the state of WebAssembly. In this article, I started by revisiting developments during 2022 to see if any of my predictions came true and if there were any surprises. Then I tried to predict where I think things will go in 2023.
  
  &lt;br /&gt;&lt;br /&gt;
  2022 didn&#39;t really feel like it had a lot of movement as far as features being released go. However, it did feel like there were a lot of things coming into place for what&#39;s to come. I think 2023 is going to be really exciting for WebAssembly and even for JavaScript.  
  
  &lt;br /&gt;&lt;br /&gt;
  The article can be found here: &lt;a href=&quot;https://platform.uno/blog/the-state-of-webassembly-2022-and-2023/&quot; target=&quot;_blank&quot;&gt;The State of WebAssembly - 2022 and 2023&lt;/a&gt;
    
  &lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
    &lt;a href=&quot;https://platform.uno/blog/the-state-of-webassembly-2022-and-2023/&quot; target=&quot;_blank&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;400&quot; data-original-height=&quot;395&quot; data-original-width=&quot;1024&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2fE_hCodl9QCnP4WEA8wxZy6oMu-Q6ii8ZDDw4WBzkoD9mYHGNYIUzvLYsfu-gBngpVWK63kFLSMKN5gGDLcvuKVt7RrR-O8TmTqkTgo6WipmjiMfqFBpbao0Wf9F3MOhKhe60ClT382TCCtXOPdQ42SszeIZBxRWICcVqYe_AXf4ZUpeUdocwINT/s1600/TSO_WebAssembly_Cover_2022-23-1024x395.jpg&quot;/&gt;
    &lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/8204464891152951946/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2023/01/the-state-of-webassembly-2022-and-2023.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/8204464891152951946'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/8204464891152951946'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2023/01/the-state-of-webassembly-2022-and-2023.html' title='The State of WebAssembly - 2022 and 2023'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjM0gvX05k0biyyIdByvPh5biXa1pDUo1PO7zCuK91qppm2vADdiLES-sDF9AXUepoCOSwTILe6-7p9paSKZjgrM9gE0tyqMUmbIdt3KjakgYQLQXkx9ITi41ilevvS0zFB04teDxVYLyCUNrzh3_AJf8IZ5AwmWIksH8acvjS6kP8urMs-5CdkLCip/s72-c/TSO_WebAssembly_Cover_1200px%20wide.jpg" height="72" width="72"/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-5887443014629488137</id><published>2022-01-11T07:30:00.005-04:00</published><updated>2023-01-30T19:55:55.192-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="State of WebAssembly"/><category scheme="http://www.blogger.com/atom/ns#" term="WebAssembly"/><title type='text'>The State of WebAssembly - 2021 and 2022</title><content type='html'>&lt;span style=&quot;display:none;&quot;&gt;The State of WebAssembly – 2021 and 2022. In this article, I show you the current state of WebAssembly by looking at the big events of 2021 and then try to predict where I think things are going in 2022.
  
  &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;395&quot; data-original-width=&quot;1200&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhAf1XAf_J3ieOxKHpH7fjGVoJYoK3A5C6dw7Ek3lCoRm2Xscn0JZcmo8PfPvJKQphW3FemFui_jgwu3RUhh5Q2eWJLuTAw3Ntm1mkJhYRq3M0-_WltJXaGi_t7YDQvhUeWEA68J9-HJehN_3xAig-PmjE4K4rEDbqxdKKjr2Iv-zqVuDxo24lhM_Aj&quot;/&gt;
&lt;/span&gt;

&lt;div style=&quot;color:black;font-family:arial;&quot;&gt;
  As one year comes to an end and a new one begins, it serves as a good opportunity to take a moment and reflect on what happened over the past year and, at the same time, try to predict what may happen in the new year.
  
  &lt;br /&gt;&lt;br /&gt;
  For the second year in a row, I&#39;ve had the privilege of writing an article on the state of WebAssembly. In the article, I talk about some of the big events of 2021 around WebAssembly and then I tell you what some of my expectations for 2022 are.
  
  &lt;br /&gt;&lt;br /&gt;
  The article can be found here: &lt;a href=&quot;https://platform.uno/blog/the-state-of-webassembly-2021-and-2022/&quot; target=&quot;_blank&quot;&gt;The State of WebAssembly - 2021 and 2022&lt;/a&gt;
  
  &lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
    &lt;a href=&quot;https://platform.uno/blog/the-state-of-webassembly-2021-and-2022/&quot; target=&quot;_blank&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;400&quot; data-original-height=&quot;395&quot; data-original-width=&quot;1024&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEisQ-7drVzZWS4LArwSftZNdMkCxgpDq0VKimhRI3vKIL_bNG6QT2ESIOPhjE9jqB4wuS4yn6CUiZNsuJ3lDPBCSgfBJ5rJvqaHOKxWCxXDg4D2Qv5-E_Vr6BghQvbfI8svtG3wgen6BDfU2i8xhc_MlUhGaZ7ygwbhNIjya2tVS8g1sPY47nbOQqN0=s400&quot;/&gt;
    &lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/5887443014629488137/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2022/01/the-state-of-webassembly-2021-and-2022.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/5887443014629488137'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/5887443014629488137'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2022/01/the-state-of-webassembly-2021-and-2022.html' title='The State of WebAssembly - 2021 and 2022'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEhAf1XAf_J3ieOxKHpH7fjGVoJYoK3A5C6dw7Ek3lCoRm2Xscn0JZcmo8PfPvJKQphW3FemFui_jgwu3RUhh5Q2eWJLuTAw3Ntm1mkJhYRq3M0-_WltJXaGi_t7YDQvhUeWEA68J9-HJehN_3xAig-PmjE4K4rEDbqxdKKjr2Iv-zqVuDxo24lhM_Aj=s72-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-8334853630797958467</id><published>2021-05-16T20:40:00.002-03:00</published><updated>2021-05-16T21:07:44.274-03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Azure"/><category scheme="http://www.blogger.com/atom/ns#" term="GitHub"/><category scheme="http://www.blogger.com/atom/ns#" term="repository"/><category scheme="http://www.blogger.com/atom/ns#" term="static web app"/><category scheme="http://www.blogger.com/atom/ns#" term="Uno Platform"/><category scheme="http://www.blogger.com/atom/ns#" term="WebAssembly"/><title type='text'>Uno Platform WebAssembly applications and Azure Static Web Apps</title><content type='html'>&lt;span style=&quot;display:none;&quot;&gt;This week, the Azure Static Web Apps service came out of preview. I was honored with the opportunity to create an article that shows you how to create an Azure Static Web App and link it to a GitHub repository containing an Uno Platform WebAssembly application.
      
  &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;665&quot; data-original-width=&quot;1270&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfKkOK59jgaWnxXe0JF61NrdnUuaok7P7y-9Y10QxczkJPXFny8W6N4YI2tTW-woMnHanqAC_efHNPgZ8MmwBaO9moA6whfsB9PP-JtK8NTcr9qumAFRuu2SXexGLL9xveYWf_ohAUpXU/s0/_ShareImage.png&quot;/&gt;
   
  &lt;!-- For use by the WebAssembly in Action GitHub readme --&gt;
  &lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;320&quot; data-original-height=&quot;344&quot; data-original-width=&quot;656&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhr4-XqcvzQel4DGWn6U-QYbhLo4I1_TTaKrjU4UXb7O9_WbrAGM3F5uYAGvqa2XX4jZggcW3E5q3UDe2LSSYw_GlLDoYkzTPlrZ6sC5kcPx8wPBxYVeqR8EkB6X_Q8vx-gRhyphenhyphenpOjvoMUQ/s320/1.+Overall+but+no+annotations.png&quot;/&gt;
  
&lt;/span&gt;


&lt;div style=&quot;color:black;font-family:arial;text-align:justify;&quot;&gt;
This week there was an &lt;a href=&quot;https://azure.microsoft.com/en-us/blog/develop-production-scale-modern-web-apps-quickly-with-azure-static-web-apps/&quot; target=&quot;_blank&quot;&gt;announcement&lt;/a&gt; that the Azure Static Web Apps service came out of preview.

&lt;br /&gt;&lt;br /&gt;
As the name implies, Azure Static Web Apps give you a way to host static web apps and it comes with many features including global distribution of your content and free SSL certificates to name a couple.

&lt;br /&gt;&lt;br /&gt;
Static web apps are applications where all the work happens in the browser and the app is decoupled from server-side code. Because an Uno Platform WebAssembly application is all client-side, it&#39;s a static web app and can take advantage of the Azure Static Web Apps service.

&lt;br /&gt;&lt;br /&gt;
I was honored with the opportunity to create an article that expands on some documentation that the Uno Platform already had on Azure Static Web Apps. In the article, I walk you through creating a GitHub repository, creating an Azure Static Web App, and then linking the two together. Then you create an Uno Platform WebAssembly application, check it into your repository, and see the Azure Static Web App automatically detect the change and deploy your new code. 

&lt;br /&gt;&lt;br /&gt;
The article can be found here: &quot;&lt;a href=&quot;https://platform.uno/blog/hosting-uno-platform-webassembly-apps-on-azure-static-web-apps/&quot; target=&quot;_blank&quot;&gt;Hosting Uno Platform WebAssembly apps on Azure Static Web Apps&lt;/a&gt;&quot;

  &lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
    &lt;a href=&quot;https://platform.uno/blog/hosting-uno-platform-webassembly-apps-on-azure-static-web-apps/&quot; target=&quot;_blank&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;400&quot; data-original-height=&quot;434&quot; data-original-width=&quot;1125&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjM3FlaobN3uI1X6bT_gyj5rnuqSgxkENpwlCy5NbsGIsJjNhEG5ln5hFO17ztjbPNfdLmTNqbgnytqLI3Aum0rVbRKCIqDw4Jec-tlc4npcLy87s-msNjk619xZIf8bjK3SHLrViEt9Go/s400/Uno_Azure_hero_v1.jpg&quot;/&gt;
    &lt;/a&gt;
  &lt;/div&gt;

&lt;/div&gt;


</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/8334853630797958467/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2021/05/uno-platform-webassembly-applications.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/8334853630797958467'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/8334853630797958467'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2021/05/uno-platform-webassembly-applications.html' title='Uno Platform WebAssembly applications and Azure Static Web Apps'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfKkOK59jgaWnxXe0JF61NrdnUuaok7P7y-9Y10QxczkJPXFny8W6N4YI2tTW-woMnHanqAC_efHNPgZ8MmwBaO9moA6whfsB9PP-JtK8NTcr9qumAFRuu2SXexGLL9xveYWf_ohAUpXU/s72-c/_ShareImage.png" height="72" width="72"/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-4799648932479424867</id><published>2021-04-14T22:34:00.005-03:00</published><updated>2021-04-14T22:40:44.941-03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term=".NET Core"/><category scheme="http://www.blogger.com/atom/ns#" term="ConFoo"/><category scheme="http://www.blogger.com/atom/ns#" term="Docker"/><category scheme="http://www.blogger.com/atom/ns#" term="Emscripten"/><category scheme="http://www.blogger.com/atom/ns#" term="IIS"/><category scheme="http://www.blogger.com/atom/ns#" term="image"/><category scheme="http://www.blogger.com/atom/ns#" term="Linux containers"/><category scheme="http://www.blogger.com/atom/ns#" term="WASI"/><category scheme="http://www.blogger.com/atom/ns#" term="WebAssembly"/><category scheme="http://www.blogger.com/atom/ns#" term="Windows containers"/><title type='text'>From ConFoo to deploying web applications with Docker</title><content type='html'>&lt;span style=&quot;display:none;&quot;&gt;The story of how discovering Docker helped me create the demos for my ConFoo 2021 presentation about the WebAssembly System Interface (WASI). Then the continued learning about Windows containers resulting in my latest Uno Platform article about Deploying C# web applications with Docker. 
  
&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;665&quot; data-original-width=&quot;1270&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1PUr-YuHvsxPKYXQPsSCMe7n94dHdZ5xTr6k1hZrwL87E7YQDrRI-aLgeUUtvdD9wHxrXNF8UvOQRqeT457hDwM3P6avm6ULk-sLmRQsHx3ix34rr8h7nIcxcM8wXnTw1Bv-7mmavKmQ/s0/_ShareImage.png&quot;/&gt;
&lt;/span&gt;

&lt;div style=&quot;color: black; font-family: arial; text-align: justify;&quot;&gt;
  
&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfYU-jW11y3bz-PvpU-kv3LqZjdSLxlGWLRHca34uNB8nsL42KlEUsr1UQEtgfWRFcHb37jIn_KwjHg9D2keU3BnOv5LxqWo1r2UdXcGvxlJDUf4MZkGmfLZSDw6h9gABduxFZ4fHXCik/s0/ConFooWASI.png&quot; style=&quot;display: block; padding: 1em 0px; text-align: center;&quot;&gt;
    &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;168&quot; data-original-width=&quot;757&quot; height=&quot;89&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfYU-jW11y3bz-PvpU-kv3LqZjdSLxlGWLRHca34uNB8nsL42KlEUsr1UQEtgfWRFcHb37jIn_KwjHg9D2keU3BnOv5LxqWo1r2UdXcGvxlJDUf4MZkGmfLZSDw6h9gABduxFZ4fHXCik/w400-h89/ConFooWASI.png&quot; width=&quot;400&quot; /&gt;
  &lt;/a&gt;
&lt;/div&gt;
    
In 2020, &lt;a href=&quot;https://confoo.ca/en&quot; target=&quot;_blank&quot;&gt;ConFoo&lt;/a&gt; opened a call for papers for their February 2021 conference. I submitted several proposals and was pleased to find out in January 2021 that one of them had been accepted.

&lt;br /&gt;&lt;br /&gt;The talk was going to be about the WebAssembly System Interface (WASI). I knew what I wanted to talk about but I wanted a couple demos that were more real-world than the typical &#39;hello world&#39; style. I had an idea for the applications I wanted to write but, to write them, I needed to compile some C libraries. Unfortunately, I was having some difficulties getting things set up on Windows.
  
&lt;br /&gt;&lt;br /&gt;My first thought for a workaround was to set up a Linux virtual machine but I was curious if there was another way. One thought that crossed my mind was, can Docker help here?
  

&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;Docker&lt;/div&gt;
  
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPEr7c2m8Ea3MyMF3-IBNU-ZRlMeNrBhQuGB0RonvXxAYFTzV0UcFeqLUCbB5XrfrnWPvcJ-FMGk9lKsf70w6kfxRh93oSB9rLjHPrxhhLgaBlPhjgaZyIC1pAYz3cRqXqf4HcaVaM470/s140/docker+logo_100px.png&quot; style=&quot;clear: right; display: block; float: right; margin-left: 1em; margin-right: 1em; text-align: center;&quot;&gt;
    &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;100&quot; data-original-width=&quot;140&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPEr7c2m8Ea3MyMF3-IBNU-ZRlMeNrBhQuGB0RonvXxAYFTzV0UcFeqLUCbB5XrfrnWPvcJ-FMGk9lKsf70w6kfxRh93oSB9rLjHPrxhhLgaBlPhjgaZyIC1pAYz3cRqXqf4HcaVaM470/s16000/docker+logo_100px.png&quot; /&gt;
  &lt;/a&gt;
&lt;/div&gt;

I had seen Docker demonstrated at a couple user group events but I hadn&#39;t used it myself so I needed to get up to speed quickly. Thankfully, I found the following YouTube video that not only explained the Docker theory but also walked through several examples that I was able to follow along with: &lt;a href=&quot;https://www.youtube.com/watch?v=3c-iBn73dDE&quot; target=&quot;_blank&quot;&gt;https://www.youtube.com/watch?v=3c-iBn73dDE&lt;/a&gt;
  
&lt;br /&gt;&lt;br /&gt;After watching the video, I started looking though the images on Docker Hub and discovered that there&#39;s a &lt;a href=&quot;https://hub.docker.com/r/emscripten/emsdk&quot; target=&quot;_blank&quot;&gt;Docker image for the Emscripten SDK&lt;/a&gt;. This is useful to me for a couple of reasons: 
&lt;ul&gt;
&amp;nbsp;&lt;li&gt;I prefer to have Emscripten use the versions of the tools it installed rather than a different version on my machine just in case the changes impact things when compiling a module. Being in a container, all the tools Emscripten needs are in the container with it allowing me to adjust the tools on my machine as needed.&lt;/li&gt;
  &lt;li&gt;I often switch between versions of Emscripten to test different things. Having an image for each version I need makes things a lot easier. Sometimes I&#39;ve had to uninstall a version of Emscripten before being able to install the next one. Now, I just need to pull and run the image with the version I need.&lt;/li&gt;
&lt;/ul&gt;
  
&lt;br /&gt;In the end, I created a Docker image derived from an Emscripten image and was able to build the demos I wanted for my ConFoo talk.
  
&lt;br /&gt;&lt;br /&gt;A few days before the ConFoo conference started, I was talking with the &lt;a href=&quot;https://platform.uno/&quot; target=&quot;_blank&quot;&gt;&lt;/a&gt;Uno Platform team about Docker. I thought Docker might be useful for C# developers for one-click deployment because I saw on Docker Hub that there was an image with IIS (Internet Information Services).

  
&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;An article about Docker&lt;/div&gt;
  
&lt;br /&gt;After the conference was over, I started looking into Docker from a C# perspective. Unfortunately, every time I tried to build a Windows Docker image, I&#39;d get errors. I spent a number of evenings reading about Windows containers and trying different things but kept spinning my wheels.
  
&lt;br /&gt;&lt;br /&gt;I learned a lot about Windows containers in the process but I felt a little dumb when I ran across an article that explained my problem. It turns out that you can&#39;t create Linux and Windows containers at the same time. You need to switch Docker Desktop to use one or the other and I was trying to create a Windows container while configured for Linux.
  
&lt;br /&gt;&lt;br /&gt;Today, I&#39;m pleased to announce that my latest article &quot;&lt;a href=&quot;https://platform.uno/blog/deploying-c-web-applications-with-docker/&quot; target=&quot;_blank&quot;&gt;Deploying C# Web Applications with Docker&lt;/a&gt;&quot; has been published on the Uno Platform&#39;s blog. The article walks you through building an image with IIS and .NET Core, publishing it to a private registry, pulling the image, and running the container.
  
  &lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
    &lt;a href=&quot;https://platform.uno/blog/deploying-c-web-applications-with-docker/&quot; target=&quot;_blank&quot; style=&quot;display: block; padding: 1em 0px; text-align: center;&quot;&gt;
      &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;434&quot; data-original-width=&quot;1125&quot; height=&quot;154&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9lvdyS0bRjMcNtymYZpwVDWNDyH-bOvRXF2HPIq1ZIkn9zriKTxReUGnk2hd9zmat33g2Z9Kj3KJcSZcB1EOt4usORfKDp-5Z5p7rb24W-U0InqnQuEz4whH43rpxfUVARP1Jp9MxaFA/w400-h154/CSharp_Docker_hero.jpg&quot; width=&quot;400&quot; /&gt;
    &lt;/a&gt;
  &lt;/div&gt;
  
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/4799648932479424867/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2021/04/from-confoo-to-deploying-web.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/4799648932479424867'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/4799648932479424867'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2021/04/from-confoo-to-deploying-web.html' title='From ConFoo to deploying web applications with Docker'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1PUr-YuHvsxPKYXQPsSCMe7n94dHdZ5xTr6k1hZrwL87E7YQDrRI-aLgeUUtvdD9wHxrXNF8UvOQRqeT457hDwM3P6avm6ULk-sLmRQsHx3ix34rr8h7nIcxcM8wXnTw1Bv-7mmavKmQ/s72-c/_ShareImage.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-7426198388229524531</id><published>2021-01-27T07:30:00.022-04:00</published><updated>2022-01-11T16:27:50.822-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="State of WebAssembly"/><category scheme="http://www.blogger.com/atom/ns#" term="WebAssembly"/><title type='text'>The State of WebAssembly - 2020 and 2021</title><content type='html'>&lt;span style=&quot;display:none;&quot;&gt;The State of WebAssembly – 2020 and 2021. In this article, I show you the current state of WebAssembly by looking at the big events of 2020 and then try to predict where I think things are going in 2021.
  
  &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;627&quot; data-original-width=&quot;1200&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEi0b9ygxUztShOxtfZTUEZrXq3PnY-dPoCVljnVc3o9PDF0hGdMx3Jb7j1nO-GpHj1480vtGOKSD4yZ4MhH3Cb9NcLYoWeXulE5AH4wJ_OsPQVaqZBWJ-WlXCM00YaP2qiXDLCl8fVJu94y6Su6Jd_pHDXxYixvtzgzNOD5kTLyeVsux7M7C946qo5A&quot;/&gt;
&lt;/span&gt;

&lt;div style=&quot;color:black;font-family:arial;text-align:justify;&quot;&gt;
  With the start of a new year, it&#39;s common to reflect on what happened over the past year and plan for the upcoming year. 
  
  &lt;br /&gt;&lt;br /&gt;
  In this article, I&#39;ll give you a quick overview of what WebAssembly and WASI are. Then I&#39;ll look at the state of WebAssembly in 2020 and tell you where I see things going this year.
  
  &lt;br /&gt;&lt;br /&gt;
  The article can be found here: &lt;a href=&quot;https://platform.uno/blog/the-state-of-webassembly-2020-and-2021/&quot; target=&quot;_blank&quot;&gt;The State of WebAssembly - 2020 and 2021&lt;/a&gt;
  
  &lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
    &lt;a href=&quot;https://platform.uno/blog/the-state-of-webassembly-2020-and-2021/&quot; target=&quot;_blank&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;
    &lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;400&quot; data-original-height=&quot;434&quot; data-original-width=&quot;1125&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgiZHQ-64AwzwqQw53xej3b6xQ83FNWKnEt_BoiddfZNUuB5rAlk0-aAGzAexI8AF68FQDiuDorYQPhqbaPJdmhEqegGZ93PGGX0Z6k_cENSYfLyaSfaMx_Cn2nnFn-8-C-J-I85DshQ_jI3KE4HQ9KtekoGV2MoAUEqLMgTe1ZCFfmPtsddzquFZ3T=s400&quot;/&gt;
    &lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/7426198388229524531/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2021/01/the-state-of-webassembly-2020-and-2021.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/7426198388229524531'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/7426198388229524531'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2021/01/the-state-of-webassembly-2020-and-2021.html' title='The State of WebAssembly - 2020 and 2021'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEi0b9ygxUztShOxtfZTUEZrXq3PnY-dPoCVljnVc3o9PDF0hGdMx3Jb7j1nO-GpHj1480vtGOKSD4yZ4MhH3Cb9NcLYoWeXulE5AH4wJ_OsPQVaqZBWJ-WlXCM00YaP2qiXDLCl8fVJu94y6Su6Jd_pHDXxYixvtzgzNOD5kTLyeVsux7M7C946qo5A=s72-c" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-7485343534095705423</id><published>2021-01-22T16:33:00.002-04:00</published><updated>2024-05-01T20:48:19.982-03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Android"/><category scheme="http://www.blogger.com/atom/ns#" term="chrome"/><category scheme="http://www.blogger.com/atom/ns#" term="COEP"/><category scheme="http://www.blogger.com/atom/ns#" term="COOP"/><category scheme="http://www.blogger.com/atom/ns#" term="Cross-Origin-Embedder-Policy"/><category scheme="http://www.blogger.com/atom/ns#" term="Cross-Origin-Opener-Policy"/><category scheme="http://www.blogger.com/atom/ns#" term="crossorigin"/><category scheme="http://www.blogger.com/atom/ns#" term="phone"/><category scheme="http://www.blogger.com/atom/ns#" term="SharedArrayBuffer"/><category scheme="http://www.blogger.com/atom/ns#" term="tablet"/><category scheme="http://www.blogger.com/atom/ns#" term="thread"/><title type='text'>WebAssembly threads arriving on Android devices</title><content type='html'>&lt;span style=&quot;display:none;&quot;&gt;WebAssembly threads arrive on Android devices! Chrome for Android (version 88) was released with the SharedArrayBuffer enabled which means you can now use WebAssembly threads on your Android phones and tablets!
  &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;327&quot; data-original-width=&quot;1200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaS_HCmqZCdFC8jP2VnXYhu_kaBmo6U3OUvBhsanV_7xgzfuqqwUucdTsEen-lF4w_ctA4RVoEjud-Szl0uskBNHKBX2AE2tbC-wLXd2an3p4hfOh1XMtAVEoRbLWWvj3Hj6VVl-AfVVA/s0/_ShareImage.png&quot;/&gt;
&lt;/span&gt;

&lt;div style=&quot;color: black; font-family: &amp;quot;arial&amp;quot;; text-align: justify;&quot;&gt;
  This week, Chrome for Android &lt;i&gt;(version 88)&lt;/i&gt; was released and will become available on Google Play over the next few weeks. With this release, the SharedArrayBuffer has been re-enabled which means you&#39;ll be able to use WebAssembly threads on your Android phones and tablets!
 
  &lt;br /&gt;&lt;br /&gt;
  &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3My3Q1DkvqI39AZ-yLjOUvKdvUqV5fyutGzeF6dkFWEVMB8nAIC15F4zuK6729eQtyMSI5xrG0KnSnBHgn57Qg5SloOZ_KSDftTBakp8saLlSI_-AnUaRKowbDUTInLf18UvmXC23wm4/s419/WebAssembly+Threading.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
      &lt;img border=&quot;0&quot; data-original-height=&quot;296&quot; data-original-width=&quot;419&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3My3Q1DkvqI39AZ-yLjOUvKdvUqV5fyutGzeF6dkFWEVMB8nAIC15F4zuK6729eQtyMSI5xrG0KnSnBHgn57Qg5SloOZ_KSDftTBakp8saLlSI_-AnUaRKowbDUTInLf18UvmXC23wm4/s0/WebAssembly+Threading.png&quot; /&gt;
    &lt;/a&gt;
  &lt;/div&gt;    

  &lt;br /&gt;
  To enable the SharedArrayBuffer, you need to specify the &lt;b&gt;Cross-Origin-Opener-Policy&lt;/b&gt; &lt;I&gt;(COOP)&lt;/I&gt; and &lt;b&gt;Cross-Origin-Embedder-Policy&lt;/b&gt; &lt;I&gt;(COEP)&lt;/I&gt; response headers. Because of the COEP response header, if you include resources from another domain that you trust, you&#39;ll need to include the &lt;b&gt;crossorigin&lt;/b&gt; attribute with those links. 

  &lt;br /&gt;&lt;br /&gt;
  In July, I wrote an article that walks you through returning the response headers, using the crossorigin attribute, and using WebAssembly threads to convert a user-supplied image to greyscale. You can find my article here: &lt;a href=&quot;https://cggallant.blogspot.com/2020/07/webassembly-threads-in-firefox.html&quot; target=&quot;_blank&quot;&gt;https://cggallant.blogspot.com/2020/07/webassembly-threads-in-firefox.html&lt;/a&gt;

  &lt;br /&gt;&lt;br /&gt;
  Although the article talks about Firefox because it was the first browser to require the new response headers, the content of the article applies to Chrome for Android too.
  
  &lt;br /&gt;&lt;br /&gt;
  The response headers will be needed for use with Chrome desktop in the near future &lt;i&gt;(version 91)&lt;/i&gt;. Safari will require them as well so it&#39;s a good idea to update your server response headers with these values if you currently use, or plan to use, the SharedArrayBuffer.
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/7485343534095705423/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2021/01/webassembly-threads-arriving-on-android.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/7485343534095705423'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/7485343534095705423'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2021/01/webassembly-threads-arriving-on-android.html' title='WebAssembly threads arriving on Android devices'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaS_HCmqZCdFC8jP2VnXYhu_kaBmo6U3OUvBhsanV_7xgzfuqqwUucdTsEen-lF4w_ctA4RVoEjud-Szl0uskBNHKBX2AE2tbC-wLXd2an3p4hfOh1XMtAVEoRbLWWvj3Hj6VVl-AfVVA/s72-c/_ShareImage.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-3285522780746304621</id><published>2021-01-13T07:37:00.005-04:00</published><updated>2021-01-13T09:28:02.084-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><category scheme="http://www.blogger.com/atom/ns#" term="ConFoo"/><category scheme="http://www.blogger.com/atom/ns#" term="WASI"/><category scheme="http://www.blogger.com/atom/ns#" term="wasm"/><category scheme="http://www.blogger.com/atom/ns#" term="WebAssembly"/><category scheme="http://www.blogger.com/atom/ns#" term="WebAssembly System Interface"/><title type='text'>ConFoo Online 2021</title><content type='html'>&lt;span style=&quot;display: none;&quot;&gt;I am very pleased to announce that I&#39;ll be speaking at ConFoo Online 2021! To flow with the conference theme, I titled my talk &quot;Joining forces to free WebAssembly from the browser&quot;.
  &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;327&quot; data-original-width=&quot;1200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJpEiaxg2ATVgBFonQHChxMYoDxZEhedBnS3ETegRQeueBkwlI4HgvyvFVklCh5MaYN-JCMRpLa2HAZR3wzT9cMn0TaBkCg0zercTgrIdAINmwsWeEHcsj8KQXyC8axL6jQFX3cYmCBmA/s0/confoo+logo_1200px+wide.png&quot; /&gt;
&lt;/span&gt;

&lt;div style=&quot;color: black; font-family: arial; text-align: justify;&quot;&gt;

  &lt;div class=&quot;separator&quot; style=&quot;clear:both;&quot;&gt;
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOZ9r8N7qm1ZVmVfFmK7kpTuG0UrS578kKgvWyBru-n5LQ0bmgjAN4WLFv6-fwz9BVlOTddS_qMMkJtFBCOqqkj-9auMm1v2nffN5NlmqLZtpCW-GiGT6FyIpOj1V9gmYuRS7KeT6M0Q8/s190/confoologo.gif&quot; style=&quot;display: block; padding: 1em 0px; text-align: center;&quot;&gt;
      &lt;img border=&quot;0&quot; data-original-height=&quot;29&quot; data-original-width=&quot;190&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOZ9r8N7qm1ZVmVfFmK7kpTuG0UrS578kKgvWyBru-n5LQ0bmgjAN4WLFv6-fwz9BVlOTddS_qMMkJtFBCOqqkj-9auMm1v2nffN5NlmqLZtpCW-GiGT6FyIpOj1V9gmYuRS7KeT6M0Q8/s16000/confoologo.gif&quot; /&gt;
    &lt;/a&gt;
  &lt;/div&gt;
  
  I am very pleased to announce that I&#39;ll be speaking at &lt;a href=&quot;https://confoo.ca/en/yul2021&quot; target=&quot;_blank&quot;&gt;ConFoo Online 2021&lt;/a&gt;! 

  &lt;br /&gt;&lt;br /&gt;As a fan of Star Wars, the organizer decided that this year&#39;s conference theme will be &quot;a new hope&quot; because of COVID-19 and the feeling that the world needs hope that things will improve. To flow with this theme, I titled my talk &lt;a href=&quot;https://confoo.ca/en/yul2021/session/joining-forces-to-free-webassembly-from-the-browser&quot; target=&quot;_blank&quot;&gt;&quot;Joining forces to free WebAssembly from the browser&quot;&lt;/a&gt;.

  &lt;br /&gt;&lt;br /&gt;In this talk you&#39;ll learn about the WebAssembly System Interface proposal (WASI) that defines a standard for using WebAssembly outside the browser in a secure way. You&#39;ll see several examples including interacting with a WebAssembly module from your C# code and at the command line.

  &lt;br /&gt;&lt;br /&gt;The conference will be virtual this year and will take place from &lt;a href=&quot;https://confoo.ca/en/yul2021&quot; target=&quot;_blank&quot;&gt;February 22nd to 26th&lt;/a&gt;. You can find the full list of sessions here: &lt;a href=&quot;https://confoo.ca/en/yul2021/sessions&quot; target=&quot;_blank&quot;&gt;https://confoo.ca/en/yul2021/sessions&lt;/a&gt;
  
  &lt;br /&gt;&lt;br /&gt;Hope to see you there.
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/3285522780746304621/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2021/01/confoo-online-2021.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/3285522780746304621'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/3285522780746304621'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2021/01/confoo-online-2021.html' title='ConFoo Online 2021'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJpEiaxg2ATVgBFonQHChxMYoDxZEhedBnS3ETegRQeueBkwlI4HgvyvFVklCh5MaYN-JCMRpLa2HAZR3wzT9cMn0TaBkCg0zercTgrIdAINmwsWeEHcsj8KQXyC8axL6jQFX3cYmCBmA/s72-c/confoo+logo_1200px+wide.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-8642462671454240176</id><published>2020-10-08T10:35:00.004-03:00</published><updated>2020-10-10T15:58:47.609-03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="@inject"/><category scheme="http://www.blogger.com/atom/ns#" term="@ref"/><category scheme="http://www.blogger.com/atom/ns#" term="BECanvas"/><category scheme="http://www.blogger.com/atom/ns#" term="Blazor"/><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><category scheme="http://www.blogger.com/atom/ns#" term="Canvas"/><category scheme="http://www.blogger.com/atom/ns#" term="DefaultRequestHeaders"/><category scheme="http://www.blogger.com/atom/ns#" term="Dovico"/><category scheme="http://www.blogger.com/atom/ns#" term="HttpClient"/><category scheme="http://www.blogger.com/atom/ns#" term="OnAfterRenderAsync"/><category scheme="http://www.blogger.com/atom/ns#" term="OnInitializedAsync"/><category scheme="http://www.blogger.com/atom/ns#" term="WebAssembly"/><title type='text'>Blazor WebAssembly and the Dovico Time Entry Status app</title><content type='html'>&lt;span style=&quot;display: none;&quot;&gt;This article will walk you through the steps to recreate Dovico Software&#39;s Time Entry Status app using Blazor WebAssembly.  
   
  &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;627&quot; data-original-width=&quot;1200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJVqiv4rjZilNzxt5xAmCTvoCPFMqbhsQsP0y3i2ENF6ZcKoOBi3yAUKos4sqWbUoeuYydI80GdY0pJnvZ3cR5O7vpYHAJXZ8Uz2LiISpABKZ37n6tKqYtATBZkE1Mb9CRDLjoWGqjAzg/s0/_ShareImage.png&quot;/&gt;
&lt;/span&gt;


&lt;div style=&quot;color: black; font-family: arial; text-align: justify;&quot;&gt;
This article covers
&lt;ul&gt;
  &lt;li&gt;Creating a Blazor WebAssembly application&lt;/li&gt;
  &lt;li&gt;Responding to events from the controls on your web page&lt;/li&gt;
  &lt;li&gt;Calling the Dovico Timesheet API&lt;/li&gt;
  &lt;li&gt;Drawing on an HTML canvas element from C#&lt;/li&gt;
&lt;/ul&gt;

&lt;br /&gt;
Recently, a customer contacted the company I work for looking for the Time Entry Status app that we used to offer on our website. It was a small Java app that I wrote in 2011 to accompany Dovico&#39;s new Timesheet API that I was creating at the time. 

&lt;br /&gt;&lt;br /&gt;
As shown in the following screen shot, the app gives you a bird&#39;s eye view of the selected employee&#39;s time over the past 30 days. Red indicates there&#39;s rejected time, black is time that hasn&#39;t been submitted, yellow is time that&#39;s awaiting approval, and blue is time that has been approved. If there&#39;s no triangle then there&#39;s no time entered for that day. 
 
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDjANCJWICkWPkDVEce42wW-uR2m07_ywvn7d27G2RYOuZcpYnbGslAeBPu98ZIS34js-ZvI5fCyuK2qoNCuesBC3zH6h-LuGBL_AtyrYdBpULN_TXpVrAi16xI1Hgqwh2kJfnHzbmH2Y/s595/0+-+ScreenShotOfTheJavaApp.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
    &lt;img border=&quot;0&quot; data-original-height=&quot;401&quot; data-original-width=&quot;595&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDjANCJWICkWPkDVEce42wW-uR2m07_ywvn7d27G2RYOuZcpYnbGslAeBPu98ZIS34js-ZvI5fCyuK2qoNCuesBC3zH6h-LuGBL_AtyrYdBpULN_TXpVrAi16xI1Hgqwh2kJfnHzbmH2Y/s16000/0+-+ScreenShotOfTheJavaApp.png&quot; width=&quot;500&quot; /&gt;
  &lt;/a&gt;
  &lt;br /&gt;(click to view the image full size)
&lt;/div&gt;
  
&lt;br /&gt;
The app is very useful if you want to quickly verify that your employees submitted their time before you run your reports or generate your invoices. It&#39;s also helpful for employees who want to quickly check to make sure that all of their time is in and has been submitted.

&lt;br /&gt;&lt;br /&gt;
Over the past few weeks, I&#39;ve been digging into WebAssembly from a C# perspective for an article that I&#39;m writing. Although I&#39;ve been following a number of WebAssembly related C# technologies like mono, the Uno Platform, and Blazor, I felt that I&#39;d get more out of my learning by building something.

&lt;br /&gt;&lt;br /&gt;
Fortunately, Dovico recently gave me some spare time to research into anything that interested me, so I decided to use that time to dig into Blazor by rewriting this application. In the process, a few adjustments were made to the timeline to modernize its look a bit as shown in the following image.

&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;clear: both; text-align: center;&quot;&gt;  
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgalLjxNn_Cvja4YjS5Hzwe1kElYuATuJzY-47Z__gNdhSPBjm-B4TKhLBA3QXWygKBm0u7W6LEh5CVPnn9CkSrtFkY_7bkJnLPRkDou2cdDsnIBs97hhecDcZDcXb7AiKzCPWE_Y53Mzw/s908/1+-+ScreenShotOfTheBlazorApp.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
    &lt;img border=&quot;0&quot; data-original-height=&quot;579&quot; data-original-width=&quot;908&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgalLjxNn_Cvja4YjS5Hzwe1kElYuATuJzY-47Z__gNdhSPBjm-B4TKhLBA3QXWygKBm0u7W6LEh5CVPnn9CkSrtFkY_7bkJnLPRkDou2cdDsnIBs97hhecDcZDcXb7AiKzCPWE_Y53Mzw/w640-h408/1+-+ScreenShotOfTheBlazorApp.png&quot; width=&quot;580&quot; /&gt;
  &lt;/a&gt;
  &lt;br /&gt;(click to view the image full size)
&lt;/div&gt;

  
&lt;br /&gt;
This article will walk you through the steps to re-create the Time Entry Status app using Blazor WebAssembly. 

&lt;br /&gt;&lt;br /&gt;
The Time Entry Status view will consist of two main elements as shown in the following image. The timeline will be drawn using an HTML Canvas element via the Blazor.Extensions.Canvas nuget package. The list of employees will use an HTML Select element.

&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5g6rfCogBoyXB-M45STJZhTGkp8SbXGgBmXiidlsW7ZoGzE0WQhqYRDOYa-vYyQj5h1Dh9IWz_DvFHgqYkRhYV6sTgFOtsrYMOrq-aBmSc3IQEvblRoyMbvlU_h5XTx52L4jBpkBhNdA/s828/2+-+sections+of+the+view.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
    &lt;img border=&quot;0&quot; data-original-height=&quot;458&quot; data-original-width=&quot;828&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5g6rfCogBoyXB-M45STJZhTGkp8SbXGgBmXiidlsW7ZoGzE0WQhqYRDOYa-vYyQj5h1Dh9IWz_DvFHgqYkRhYV6sTgFOtsrYMOrq-aBmSc3IQEvblRoyMbvlU_h5XTx52L4jBpkBhNdA/s16000/2+-+sections+of+the+view.png&quot; width=&quot;580&quot; /&gt;
  &lt;/a&gt;
  &lt;br /&gt;(click to view the image full size)
&lt;/div&gt;
 
  
&lt;br /&gt;
As shown below, the following are the steps for building this application:
&lt;ol&gt;
  &lt;li&gt;Create a Blazor WebAssembly project&lt;/li&gt;
  &lt;li&gt;Install and configure the Blazor.Extensions.Canvas nuget package that will make interacting with a canvas element easier from C#&lt;/li&gt;
  &lt;li&gt;Create the models for Dovico Timesheet&#39;s data&lt;/li&gt;
  &lt;li&gt;Build the Time Entry Status view&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidavG2ExtMRlDVqfcqHw9DNmy-qdR1IejPJ25sZ9vMFN3y3XfEEt6esPyT5ynYXLK1xwXNU9Lce-l4LfsUGCk8xJLvGnGnJiDJyNtPkvO53J26RMvvCG_lZMHslp1w2EoNIAswc_pMBl8/s857/3+-+steps+needed+for+the+article.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
    &lt;img border=&quot;0&quot; data-original-height=&quot;677&quot; data-original-width=&quot;857&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidavG2ExtMRlDVqfcqHw9DNmy-qdR1IejPJ25sZ9vMFN3y3XfEEt6esPyT5ynYXLK1xwXNU9Lce-l4LfsUGCk8xJLvGnGnJiDJyNtPkvO53J26RMvvCG_lZMHslp1w2EoNIAswc_pMBl8/s16000/3+-+steps+needed+for+the+article.png&quot; width=&quot;580&quot; /&gt;
  &lt;/a&gt;
  &lt;br /&gt;(click to view the image full size)
&lt;/div&gt;


  
&lt;br /&gt;&lt;br /&gt;
As the following image shows, your first step is to create a Blazor WebAssembly project.

&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjuE3n9mkZZrs0utxm3qGWCObGvfj6YWWhZVwXDAgTAJX4B9i43t9SWRBm_cDY4dzqjY-meaHqsfnWEOpBv6DB8VbBC0n8spD4v224MclyKBfJxS3CLzklbktppjvYj4JKMaswMJYLg0U/s0/4+-+step+1+-+create+the+project.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
    &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;173&quot; data-original-width=&quot;585&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjuE3n9mkZZrs0utxm3qGWCObGvfj6YWWhZVwXDAgTAJX4B9i43t9SWRBm_cDY4dzqjY-meaHqsfnWEOpBv6DB8VbBC0n8spD4v224MclyKBfJxS3CLzklbktppjvYj4JKMaswMJYLg0U/s0/4+-+step+1+-+create+the+project.png&quot; width=&quot;380&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;
  
&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;1. Create a Blazor WebAssembly project&lt;/div&gt;

&lt;br /&gt;
This article will be using Visual Studio 2019 to build the application, but Visual Studio Code can also be used. If you’d prefer to use Visual Studio Code, the following web page walks you through the steps to create a Blazor WebAssembly project using that IDE: &lt;a href=&quot;https://docs.microsoft.com/en-us/learn/modules/build-blazor-webassembly-visual-studio-code/&quot; target=&quot;_blank&quot;&gt;https://docs.microsoft.com/en-us/learn/modules/build-blazor-webassembly-visual-studio-code/&lt;/a&gt;

&lt;br /&gt;&lt;br /&gt;
Open &lt;i&gt;Visual Studio 2019&lt;/i&gt; and choose &lt;b&gt;Create a new project&lt;/b&gt;
&lt;ul&gt;
  &lt;li&gt;Under &lt;i&gt;C#&lt;/i&gt;, choose &lt;b&gt;Blazor App&lt;/b&gt; and click &lt;b&gt;Next&lt;/b&gt;.&lt;/li&gt;
  &lt;li&gt;Give the project a name. I called mine &lt;i&gt;BlazorTimeEntryStatus&lt;/i&gt; but you can use a different name if you&#39;d like. Click &lt;b&gt;Create&lt;/b&gt;.&lt;/li&gt;
  &lt;li&gt;At this point, you&#39;ll be presented with a second dialog as shown in the following image. Select &lt;b&gt;Blazor WebAssembly App&lt;/b&gt; and then click &lt;b&gt;Create&lt;/b&gt;.
  
    &lt;br /&gt;&lt;br /&gt;
    &lt;div class=&quot;separator&quot; style=&quot;clear:both;text-align: center;&quot;&gt;
      &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxq2ewHEaU2Towo1v4VNu3oj57DGaoan0UYmjyuZ-V9lqsdN7FbjwKS6FlVWvwxKKGBoWd9IL90zDenDhJu5gB6hJpXySs5ayBD-gjCAcYfs5szzvpK-xItBfmtE7hyINWfYkepTC0km8/s0/5+-+VS+New+Blazor+Project.png&quot;&gt;
        &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;710&quot; data-original-width=&quot;1024&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxq2ewHEaU2Towo1v4VNu3oj57DGaoan0UYmjyuZ-V9lqsdN7FbjwKS6FlVWvwxKKGBoWd9IL90zDenDhJu5gB6hJpXySs5ayBD-gjCAcYfs5szzvpK-xItBfmtE7hyINWfYkepTC0km8/s0/5+-+VS+New+Blazor+Project.png&quot; width=&quot;550&quot;/&gt;
      &lt;/a&gt;
    &lt;/div&gt;    
  &lt;/li&gt;
&lt;/ul&gt;

&lt;br /&gt;
The application you&#39;re about to build will be pulling its information from a Dovico Timesheet demo database via the Dovico Timesheet API. Signing up for a demo database is free and is good for 30 days. To sign up, click the &lt;b&gt;Try for free&lt;/b&gt; button at the top of the Dovico website: &lt;a href=&quot;https://www.dovico.com/&quot; target=&quot;_blank&quot;&gt;https://www.dovico.com/&lt;/a&gt;

&lt;br /&gt;&lt;br /&gt;
The Dovico Timesheet API expects every call to include an &lt;i&gt;Authorization&lt;/i&gt; header in the following format:
&lt;div class=&quot;divExample&quot;&gt;
  WRAP access_token=&quot;client=&lt;b&gt;ConsumerSecret&lt;/b&gt;&amp;amp;user_token=&lt;b&gt;DataAccessToken&lt;/b&gt;&quot;
&lt;/div&gt;

&lt;br /&gt;
The &lt;i&gt;Consumer secret&lt;/i&gt; and &lt;i&gt;Data access token&lt;/i&gt; values can be found in the &lt;b&gt;Company&lt;/b&gt; view (&lt;i&gt;Setup&lt;/i&gt; menu) of Dovico Timesheet in the &lt;b&gt;API&lt;/b&gt; tab. 

&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divIndentBothSides&quot;&gt;
  &lt;b&gt;WARNING:&lt;/b&gt; The data access token found in the Company view is a system-wide access token with full access to all data and that might not be what you want depending on your use for the API. Each employee also has a data access token that restricts data to only what their security settings allow. The employee data access token of the logged in user can be found by moving your mouse over your name in the top-right corner of Dovico Timesheet and choosing &lt;i&gt;Settings&lt;/i&gt; from the drop-down menu.
&lt;/div&gt;

&lt;br /&gt;
The Dovico Timesheet API was designed when a lot of applications were still using XML and, as a result, the data is returned in XML format by default. To change the response to JSON, you&#39;ll need to include an &lt;i&gt;Accept&lt;/i&gt; header with the value &lt;i&gt;application/json&lt;/i&gt;. For more information about the Dovico Timesheet API, the documentation can be found at the following website: &lt;a href=&quot;https://www.dovico.com/developer/API_doc/&quot; target=&quot;_blank&quot;&gt;https://www.dovico.com/developer/API_doc/&lt;/a&gt;
  
&lt;br /&gt;&lt;br /&gt;
Rather than have your code specify the &lt;i&gt;Authorization&lt;/i&gt; and &lt;i&gt;Accept&lt;/i&gt; headers every time you send a request to the API, you&#39;ll modify the &lt;i&gt;HttpClient&lt;/i&gt; object in the &lt;i&gt;Program.cs&lt;/i&gt; file, to specify the headers in the &lt;i&gt;DefaultRequestHeaders&lt;/i&gt; property. Once set, every HTTP request your application makes will include the request headers automatically.

&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divIndentBothSides&quot;&gt;
  &lt;b&gt;WARNING:&lt;/b&gt; Your Blazor WebAssembly code might be written in C# but it will be downloaded and run in the browser. You shouldn&#39;t include anything in your codebase that you don&#39;t want someone outside your organization seeing. Also, even though your code will be using the HttpClient class, under the hood it will be using the browser&#39;s Fetch API and it&#39;s very easy to inspect HTTP requests from a browser using the browser&#39;s built-in developer tools. I&#39;ll write articles in the future that dig into securing the HTTP requests but for now the following website has some tips: &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/?view=aspnetcore-3.1&quot; target=&quot;_blank&quot;&gt;https://docs.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/?view=aspnetcore-3.1&lt;/a&gt;
&lt;/div&gt;

&lt;br /&gt;
Open the &lt;i&gt;Program.cs&lt;/i&gt; file and adjust the &lt;i&gt;Main&lt;/i&gt; function to now have the code shown in the following code snippet. Note that the &lt;i&gt;AccessToken&lt;/i&gt; string shown in the following snippet will need to be adjusted to match the values from your demo database as the ones shown here might be expired depending on when you read this article.
  
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;public static&lt;/span&gt; async Task Main(&lt;span style=&quot;color:blue;&quot;&gt;string&lt;/span&gt;[] args)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;var&lt;/span&gt; builder = WebAssemblyHostBuilder.CreateDefault(args);
    &lt;br /&gt;builder.RootComponents.Add&amp;lt;app&amp;gt;(&lt;span style=&quot;color:maroon;&quot;&gt;&quot;app&quot;&lt;/span&gt;);

    &lt;br /&gt;&lt;br /&gt;
    HttpClient Http = &lt;span style=&quot;color:blue;&quot;&gt;new&lt;/span&gt; HttpClient 
    &lt;br /&gt;{ 
    &lt;div class=&quot;divIndent&quot;&gt;
      BaseAddress = &lt;span style=&quot;color:blue;&quot;&gt;new&lt;/span&gt; Uri(builder.HostEnvironment.BaseAddress) 
    &lt;/div&gt;
    };

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Set the default request headers for all HTTP calls&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;string&lt;/span&gt; AccessToken = 
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color:maroon;&quot;&gt;&quot;access_token=\&quot;client=a09af4b31071467dbd1730cd19a6b162.145303&amp;amp;user_token=3aa1215e1e2b447bb8458d7997d0a011.145303\&quot;&quot;&lt;/span&gt;;
          
    &lt;br /&gt;Http.DefaultRequestHeaders.Authorization = &lt;span style=&quot;color:blue;&quot;&gt;new&lt;/span&gt; AuthenticationHeaderValue(&lt;span style=&quot;color:maroon;&quot;&gt;&quot;WRAP&quot;&lt;/span&gt;,
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AccessToken);
    
    &lt;br /&gt;Http.DefaultRequestHeaders.Accept.Clear();
    &lt;br /&gt;Http.DefaultRequestHeaders.Accept.Add(
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color:blue;&quot;&gt;new&lt;/span&gt; MediaTypeWithQualityHeaderValue(&lt;span style=&quot;color:maroon;&quot;&gt;&quot;application/json&quot;&lt;/span&gt;));
    
    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Add the HttpClient to the services collection&lt;/span&gt;
    &lt;br /&gt;builder.Services.AddScoped(sp =&amp;gt; Http);

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; builder.Build().RunAsync();
  &lt;/div&gt;
  }
&lt;/div&gt;

&lt;br /&gt;
You will also need to include the following using statement at the beginning of the &lt;i&gt;Program.cs&lt;/i&gt; file:
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;using&lt;/span&gt; System.Net.Http.Headers;
&lt;/div&gt;


  
&lt;br /&gt;&lt;br /&gt;
With the project created and the default HTTP request headers specified, your next step as shown in the following image, is to install and configure the &lt;i&gt;Blazor.Extensions.Canvas&lt;/i&gt; nuget package. This package will make interacting with an HTML Canvas element easier from C#.
 
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear:both;text-align: center;&quot;&gt;
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjX_pZ4CJGx51BKsLK0OEdNwx5gXi6t8m4X0oRJM0ZZkbBJtXE9NdhRmJGq2r1w7Ds60rjx_4-tWqg9_ZKTqjZFmGHynC8IVJqvdzLQvAKkvVf01izKLDkUgXax4Sl8a6P7CxhhD7mFAz0/s0/6+-+step+2+-+nuget+package.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
    &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;209&quot; data-original-width=&quot;509&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjX_pZ4CJGx51BKsLK0OEdNwx5gXi6t8m4X0oRJM0ZZkbBJtXE9NdhRmJGq2r1w7Ds60rjx_4-tWqg9_ZKTqjZFmGHynC8IVJqvdzLQvAKkvVf01izKLDkUgXax4Sl8a6P7CxhhD7mFAz0/s0/6+-+step+2+-+nuget+package.png&quot; width=&quot;380&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;
 

&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;2. The Blazor.Extensions.Canvas nuget package&lt;/div&gt;

&lt;br /&gt;  
The timeline portion of the view, as shown in the following image, will be drawn using an HTML canvas element and will be adjusted from C# by using the Blazor.Extensions.Canvas nuget package. More information about this package can be found by visiting the following web page: &lt;a href=&quot;https://www.nuget.org/packages/Blazor.Extensions.Canvas/1.0.0&quot; target=&quot;_blank&quot;&gt;https://www.nuget.org/packages/Blazor.Extensions.Canvas/1.0.0&lt;/a&gt;

&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear:both;text-align:center;&quot;&gt;
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXRzKz2McXWTql-uE0sRo5vrqodwqTRGI6SlHRcEeI-VrwBhVMsMYO1xZ8Em7fdauzd-YiV0F-22fKxALh2BN_rLY_NPWdhoz7YFa-o87vcjRIMZNEzK1dQD9BpKobfAP-7qthJHllS0o/s0/7+-+The+timeline+canvas.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
    &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;163&quot; data-original-width=&quot;636&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXRzKz2McXWTql-uE0sRo5vrqodwqTRGI6SlHRcEeI-VrwBhVMsMYO1xZ8Em7fdauzd-YiV0F-22fKxALh2BN_rLY_NPWdhoz7YFa-o87vcjRIMZNEzK1dQD9BpKobfAP-7qthJHllS0o/s0/7+-+The+timeline+canvas.png&quot; width=&quot;500&quot; /&gt;
  &lt;/a&gt;
&lt;/div&gt;

  
&lt;br /&gt;
To install the package, click on the &lt;b&gt;Tools&lt;/b&gt;, &lt;b&gt;NuGet Package Manager&lt;/b&gt;, &lt;b&gt;Package Manager Console&lt;/b&gt; menu item and then run the following command:
&lt;div class=&quot;divExample&quot;&gt;
  Install-Package Blazor.Extensions.Canvas -Version 1.0.0
&lt;/div&gt;
  
&lt;br /&gt;
Open your &lt;i&gt;_Imports.razor&lt;/i&gt; file and add the following using statement after the other using statements:
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;background-color:#FFFF00&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:blue;&quot;&gt;using&lt;/span&gt; Blazor.Extensions.Canvas
&lt;/div&gt;

&lt;br /&gt;
In your &lt;i&gt;Solution Explorer&lt;/i&gt;, expand the &lt;i&gt;wwwroot&lt;/i&gt; folder, and then open the &lt;i&gt;index.html&lt;/i&gt; file. Add the following script tag just before the &lt;i&gt;&amp;lt;/head&amp;gt;&lt;/i&gt; tag:
&lt;div class=&quot;divExample&quot;&gt;
  &amp;lt;script src=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;_content/Blazor.Extensions.Canvas/blazor.extensions.canvas.js&quot;&lt;/span&gt;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/div&gt;


  
&lt;br /&gt;&lt;br /&gt;
With the Blazor.Extensions.Canvas nuget package installed and configured your next step, as shown in the following image, is to define the models for Dovico Timesheet&#39;s data.

&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear:both;text-align:center;&quot;&gt;
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGivCUF7WUKRva7C34ZxeOeRpYVAlkx64mJ08ePZRNRRpXINdmNU6bSA97sRcYW0eW1sZeLGSx529STlPr315zxas9OT3umfxp4wUppy8j-_JJHh43cNl-quvFrpkOOmNS2QDrem_8fzc/s0/8+-+step+3+-+add+models+for+Dovico+data.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
    &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;220&quot; data-original-width=&quot;685&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGivCUF7WUKRva7C34ZxeOeRpYVAlkx64mJ08ePZRNRRpXINdmNU6bSA97sRcYW0eW1sZeLGSx529STlPr315zxas9OT3umfxp4wUppy8j-_JJHh43cNl-quvFrpkOOmNS2QDrem_8fzc/s0/8+-+step+3+-+add+models+for+Dovico+data.png&quot; width=&quot;500&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;3. Create the models for Dovico Timesheet&#39;s data&lt;/div&gt;

&lt;br /&gt;
When the view is first displayed, it will call the Dovico Timesheet API for the list of employees. If you use an individual employee&#39;s data access token for the call, the employee might not have permission to the &lt;i&gt;Employees&lt;/i&gt; endpoint. If the request for employees fails with an &lt;i&gt;Unauthorized&lt;/i&gt; status code, the view will then call the &lt;i&gt;Employees/Me&lt;/i&gt; endpoint for the logged in employee&#39;s basic information. 

&lt;br /&gt;&lt;br /&gt;
The response data returned from both endpoints will be similar, but the &lt;i&gt;Employees/Me&lt;/i&gt; response data will only hold the employee&#39;s &lt;i&gt;ID&lt;/i&gt;, &lt;i&gt;first name&lt;/i&gt;, and &lt;i&gt;last name&lt;/i&gt;. The &lt;i&gt;PrevPageURI&lt;/i&gt;, &lt;i&gt;NextPageURI&lt;/i&gt;, and &lt;i&gt;WorkDays&lt;/i&gt; properties won&#39;t be present in the Employees/Me response data. 

&lt;br /&gt;&lt;br /&gt;
The &lt;i&gt;WorkDays&lt;/i&gt; value is used by the view to give a slightly darker background on the timeline for the days that the employee isn&#39;t scheduled to work. The Employee model will set the WorkDays default value to indicate that Saturday and Sunday are non-working days in the event the API call does not return the WorkDays property.

&lt;br /&gt;&lt;br /&gt;
In the &lt;i&gt;Solution Explorer&lt;/i&gt;, right-click on your project and choose &lt;b&gt;Add&lt;/b&gt;, &lt;b&gt;New Folder&lt;/b&gt; from the context menu. Give the folder the name &lt;i&gt;Models&lt;/i&gt;.

&lt;br /&gt;&lt;br /&gt;
Right-click on the &lt;i&gt;Models&lt;/i&gt; folder and choose &lt;b&gt;Add&lt;/b&gt;, &lt;b&gt;Class...&lt;/b&gt; from the context menu. Give the class the name &lt;i&gt;Employee&lt;/i&gt;.

&lt;br /&gt;&lt;br /&gt;
In your new &lt;i&gt;Employee.cs&lt;/i&gt; file, replace the generated &lt;i&gt;Employee&lt;/i&gt; class with the code from the following snippet:

&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;public class&lt;/span&gt; EmployeeResponse
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;public&lt;/span&gt; List&amp;lt;Employee&amp;gt; Employees { &lt;span style=&quot;color:blue;&quot;&gt;get&lt;/span&gt;; &lt;span style=&quot;color:blue;&quot;&gt;set&lt;/span&gt;; }

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// NOTE: An Employees/Me/ call will not include the following properties in its response&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;public string&lt;/span&gt; PrevPageURI { &lt;span style=&quot;color:blue;&quot;&gt;get&lt;/span&gt;; &lt;span style=&quot;color:blue;&quot;&gt;set&lt;/span&gt;; }
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;public string&lt;/span&gt; NextPageURI { &lt;span style=&quot;color:blue;&quot;&gt;get&lt;/span&gt;; &lt;span style=&quot;color:blue;&quot;&gt;set&lt;/span&gt;; }
  &lt;/div&gt;
  }

  &lt;br /&gt;&lt;br /&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;public class&lt;/span&gt; Employee
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// If the logged in user doesn&#39;t have permission to call Employees/, you&#39;ll call&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// Employees/Me/ instead. Unfortunately, that call doesn&#39;t return a WorkDays value so you&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// need to make sure the _WorkDaysArray is populated with a default value.&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;public&lt;/span&gt; Employee() { SetWorkDays(_WorkDays); }

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;public string&lt;/span&gt; ID { &lt;span style=&quot;color:blue;&quot;&gt;get&lt;/span&gt;; &lt;span style=&quot;color:blue;&quot;&gt;set&lt;/span&gt;; }
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;public string&lt;/span&gt; FirstName { &lt;span style=&quot;color:blue;&quot;&gt;get&lt;/span&gt;; &lt;span style=&quot;color:blue;&quot;&gt;set&lt;/span&gt;; }
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;public string&lt;/span&gt; LastName { &lt;span style=&quot;color:blue;&quot;&gt;get&lt;/span&gt;; &lt;span style=&quot;color:blue;&quot;&gt;set&lt;/span&gt;; }

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Indicates which days the employee works (can be different for each employee). The dot&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// indicates a non-working day.&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;protected string&lt;/span&gt; _WorkDays = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;.MTWTF.&quot;&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;public string&lt;/span&gt; WorkDays
    &lt;br /&gt;{
    &lt;div class=&quot;divIndent&quot;&gt;
      &lt;span style=&quot;color:blue;&quot;&gt;get&lt;/span&gt; { &lt;span style=&quot;color:blue;&quot;&gt;return&lt;/span&gt; _WorkDays; }
      &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;set&lt;/span&gt; { SetWorkDays(&lt;span style=&quot;color:blue;&quot;&gt;value&lt;/span&gt;); }
    &lt;/div&gt;
    }

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;protected&lt;/span&gt; List&amp;lt;&lt;span style=&quot;color:blue;&quot;&gt;bool&lt;/span&gt;&amp;gt; _WorkDaysArray = &lt;span style=&quot;color:blue;&quot;&gt;new&lt;/span&gt; List&amp;lt;&lt;span style=&quot;color:blue;&quot;&gt;bool&lt;/span&gt;&amp;gt;();
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;protected void&lt;/span&gt; SetWorkDays(&lt;span style=&quot;color:blue;&quot;&gt;string&lt;/span&gt; WorkDays)
    &lt;br /&gt;{
    &lt;div class=&quot;divIndent&quot;&gt;
      &lt;span style=&quot;color:darkgreen;&quot;&gt;// Remember the original string&lt;/span&gt;
      &lt;br /&gt;_WorkDays = WorkDays;

      &lt;br /&gt;&lt;br /&gt;
      &lt;span style=&quot;color:darkgreen;&quot;&gt;// Create the WorkDays array. Each day that is not a dot is a workday.&lt;/span&gt;
      &lt;br /&gt;_WorkDaysArray.Clear();
      &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;foreach&lt;/span&gt; (&lt;span style=&quot;color:blue;&quot;&gt;char&lt;/span&gt; Day &lt;span style=&quot;color:blue;&quot;&gt;in&lt;/span&gt; WorkDays) { _WorkDaysArray.Add((Day != &lt;span style=&quot;color:maroon;&quot;&gt;&#39;.&#39;&lt;/span&gt;)); }
    &lt;/div&gt;
    }

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// DayOfWeek is a zero-based value starting with Sunday&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;public bool&lt;/span&gt; IsWorkingDay(DayOfWeek Day) { &lt;span style=&quot;color:blue;&quot;&gt;return&lt;/span&gt; _WorkDaysArray[(&lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt;)Day]; }
  &lt;/div&gt;
  }
&lt;/div&gt;
  
&lt;br /&gt;
The Time Entry Status view will present the employees to the user in an HTML Select element. When the user selects an employee, that employee&#39;s time information will be requested from the Dovico Timesheet API to render the timeline.

&lt;br /&gt;&lt;br /&gt;
Your next step is to create the models for the time entry information that you&#39;ll need from the API. 

&lt;br /&gt;&lt;br /&gt;
The API will return the time entry&#39;s date as a string in the format &lt;i&gt;yyyy-MM-dd&lt;/i&gt;. To make working with the data easier, your model will convert the string into a &lt;i&gt;DateTime&lt;/i&gt; object.

&lt;br /&gt;&lt;br /&gt;
Right-click on your &lt;i&gt;Models&lt;/i&gt; folder again and choose &lt;b&gt;Add&lt;/b&gt;, &lt;b&gt;Class...&lt;/b&gt; from the context menu. Give the class the name &lt;i&gt;TimeEntry&lt;/i&gt;.

&lt;br /&gt;&lt;br /&gt;
In your &lt;i&gt;TimeEntry.cs&lt;/i&gt; file, replace the generated &lt;i&gt;TimeEntry&lt;/i&gt; class with the code from the following snippet:

&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;public class&lt;/span&gt; TimeEntryResponse
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;public&lt;/span&gt; List&amp;lt;TimeEntry&amp;gt; TimeEntries { &lt;span style=&quot;color:blue;&quot;&gt;get&lt;/span&gt;; &lt;span style=&quot;color:blue;&quot;&gt;set&lt;/span&gt;; }

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;public string&lt;/span&gt; PrevPageURI { &lt;span style=&quot;color:blue;&quot;&gt;get&lt;/span&gt;; &lt;span style=&quot;color:blue;&quot;&gt;set&lt;/span&gt;; }
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;public string&lt;/span&gt; NextPageURI { &lt;span style=&quot;color:blue;&quot;&gt;get&lt;/span&gt;; &lt;span style=&quot;color:blue;&quot;&gt;set&lt;/span&gt;; }
  &lt;/div&gt;
  }

  &lt;br /&gt;&lt;br /&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;public class&lt;/span&gt; TimeEntry
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;public string&lt;/span&gt; ID { &lt;span style=&quot;color:blue;&quot;&gt;get&lt;/span&gt;; &lt;span style=&quot;color:blue;&quot;&gt;set&lt;/span&gt;; }
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;public&lt;/span&gt; Sheet Sheet { &lt;span style=&quot;color:blue;&quot;&gt;get&lt;/span&gt;; &lt;span style=&quot;color:blue;&quot;&gt;set&lt;/span&gt;; }

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;protected string&lt;/span&gt; _DateString = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;&quot;&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;public string&lt;/span&gt; Date
    &lt;br /&gt;{
    &lt;div class=&quot;divIndent&quot;&gt;
      &lt;span style=&quot;color:blue;&quot;&gt;get&lt;/span&gt; { &lt;span style=&quot;color:blue;&quot;&gt;return&lt;/span&gt; _DateString; }
      &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;set&lt;/span&gt; { SetDate(&lt;span style=&quot;color:blue;&quot;&gt;value&lt;/span&gt;); }
    &lt;/div&gt;
    }

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Needed so that you can sort and compare based on the date value&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;protected&lt;/span&gt; DateTime _Date = DateTime.MinValue;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;public&lt;/span&gt; DateTime TheDate { &lt;span style=&quot;color:blue;&quot;&gt;get&lt;/span&gt; { &lt;span style=&quot;color:blue;&quot;&gt;return&lt;/span&gt; _Date; } }

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;protected void&lt;/span&gt; SetDate(&lt;span style=&quot;color:blue;&quot;&gt;string&lt;/span&gt; Date)
    &lt;br /&gt;{
    &lt;div class=&quot;divIndent&quot;&gt;
      &lt;span style=&quot;color:darkgreen;&quot;&gt;// Remember the original string&lt;/span&gt;
      &lt;br /&gt;_DateString = Date;

      &lt;br /&gt;&lt;br /&gt;
      _Date = DateTime.ParseExact(Date, &lt;span style=&quot;color:maroon;&quot;&gt;&quot;yyyy-MM-dd&quot;&lt;/span&gt;, CultureInfo.InvariantCulture);
    &lt;/div&gt;
    }
  &lt;/div&gt;
  }

  &lt;br /&gt;&lt;br /&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;public class&lt;/span&gt; Sheet
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;public string&lt;/span&gt; ID { &lt;span style=&quot;color:blue;&quot;&gt;get&lt;/span&gt;; &lt;span style=&quot;color:blue;&quot;&gt;set&lt;/span&gt;; }
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;public string&lt;/span&gt; Status { &lt;span style=&quot;color:blue;&quot;&gt;get&lt;/span&gt;; &lt;span style=&quot;color:blue;&quot;&gt;set&lt;/span&gt;; }
  &lt;/div&gt;
  }
&lt;/div&gt;

&lt;br /&gt;
You need to include the following using statement at the beginning of the &lt;i&gt;TimeEntry.cs&lt;/i&gt; file because of the use of the &lt;i&gt;CultureInfo&lt;/i&gt; class when parsing the date:
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;using&lt;/span&gt; System.Globalization;
&lt;/div&gt;


  
&lt;br /&gt;&lt;br /&gt;
With the models now created, you&#39;re ready to build the Time Entry Status view itself which is your next step as shown in the following image.
 
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear:both;text-align:center;&quot;&gt;
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhF5MGboF5keun3df99vDKZjaUz-627lZsQrkaYeBiD3MC_gfD-1j7DW651hdjFPtrQYpRqBg9Vgm31HU8VICTUu2pxnR97VDh4KX-DXnuhhICHxXxrwOlsBtq18meCpogdq7zNyPdeYw0/s0/9+-+step+4+-+build+the+view.png&quot; style=&quot;margin-left:1em;margin-right:1em;&quot;&gt;
    &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;650&quot; data-original-width=&quot;825&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhF5MGboF5keun3df99vDKZjaUz-627lZsQrkaYeBiD3MC_gfD-1j7DW651hdjFPtrQYpRqBg9Vgm31HU8VICTUu2pxnR97VDh4KX-DXnuhhICHxXxrwOlsBtq18meCpogdq7zNyPdeYw0/s0/9+-+step+4+-+build+the+view.png&quot; width=&quot;580&quot; /&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;4. Build the Time Entry Status view&lt;/div&gt;

&lt;br /&gt;
Rather than creating a new page for the Time Entry Status view, you&#39;ll modify the generated &lt;i&gt;Index.razor&lt;/i&gt; file.

&lt;br /&gt;&lt;br /&gt;
Blazor uses razor pages which are a combination of razor markup, HTML and C# code. More information about razor pages can be found on the following web page: &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/mvc/views/razor?view=aspnetcore-3.1&quot; target=&quot;_blank&quot;&gt;https://docs.microsoft.com/en-us/aspnet/core/mvc/views/razor?view=aspnetcore-3.1&lt;/a&gt;
  
&lt;br /&gt;&lt;br /&gt;
In the &lt;i&gt;Solution Explorer&lt;/i&gt;, expand the &lt;i&gt;Pages&lt;/i&gt; folder, and open the &lt;i&gt;Index.razor&lt;/i&gt; file. Delete everything from the file after the &lt;i&gt;@Page&lt;/i&gt; declaration.

&lt;br /&gt;&lt;br /&gt;
The first thing that you need to add to the page are the &lt;i&gt;using&lt;/i&gt; statements for the models that you just built as well as the Blazor.Extensions.Canvas objects.

&lt;br /&gt;&lt;br /&gt;
In a razor page, the &lt;i&gt;using&lt;/i&gt; statement is prefixed with an &lt;i&gt;@&lt;/i&gt; character and the semicolon at the end of the statement is optional. Add the following code to your &lt;i&gt;Index.razor&lt;/i&gt; file after the &lt;i&gt;@Page&lt;/i&gt; declaration:

&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;background-color:#FFFF00&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:blue;&quot;&gt;using&lt;/span&gt; BlazorTimeEntryStatus.Models;
  &lt;br /&gt;&lt;span style=&quot;background-color:#FFFF00&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:blue;&quot;&gt;using&lt;/span&gt; Blazor.Extensions;
  &lt;br /&gt;&lt;span style=&quot;background-color:#FFFF00&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:blue;&quot;&gt;using&lt;/span&gt; Blazor.Extensions.Canvas.Canvas2D;
  &lt;br /&gt;&lt;span style=&quot;background-color:#FFFF00&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:blue;&quot;&gt;using&lt;/span&gt; Blazor.Extensions.Canvas.Model;
&lt;/div&gt;
    
&lt;br /&gt;
&lt;div class=&quot;divIndentBothSides&quot;&gt;
  &lt;b&gt;NOTE:&lt;/b&gt; If you used a different name for your project, the namespace for the Models in the previous snippet will need to be adjusted to match your namespace.
&lt;/div&gt;
  
&lt;br /&gt;
In your &lt;i&gt;Program.cs&lt;/i&gt; file, the &lt;i&gt;HttpClient&lt;/i&gt; object was added as a dependency. To access the object in the view, you need to inject the service by using the &lt;i&gt;@inject&lt;/i&gt; directive. Add the code in the following snippet after your &lt;i&gt;@using&lt;/i&gt; statements in your &lt;i&gt;Index.razor&lt;/i&gt; file:
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;background-color:#FFFF00&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:blue;&quot;&gt;inject&lt;/span&gt; HttpClient Http&lt;span style=&quot;background-color:#FFFF00&quot;&gt;;&lt;/span&gt;
&lt;/div&gt;

&lt;br /&gt;
Next, you&#39;ll include an &lt;i&gt;H2&lt;/i&gt; element to give the view the title &lt;i&gt;Time Entry Status&lt;/i&gt;. 

&lt;br /&gt;&lt;br /&gt;
After the &lt;i&gt;H2&lt;/i&gt; element, you&#39;ll create a &lt;i&gt;Div&lt;/i&gt; element to hold the timeline which is an &lt;i&gt;HTML Canvas&lt;/i&gt; element represented by the &lt;i&gt;BECanvas&lt;/i&gt; object (&lt;i&gt;Blazor.Extensions.Canvas&lt;/i&gt;). You&#39;ll include an &lt;i&gt;@ref=&quot;_canvasReference&quot;&lt;/i&gt; attribute as part of the BECanvas element so that, when the HTML element is created, your code&#39;s &lt;i&gt;_canvasReference&lt;/i&gt; variable will be given a reference to the &lt;i&gt;BECanvas&lt;/i&gt; element so that you can interact with it.

&lt;br /&gt;&lt;br /&gt;
Add the code in the following snippet after your &lt;i&gt;@inject&lt;/i&gt; directive in your &lt;i&gt;Index.razor&lt;/i&gt; file:
  
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &amp;lt;h2&amp;gt;Time Entry Status&amp;lt;/h2&amp;gt;

  &lt;br /&gt;&lt;br /&gt;
  &amp;lt;div class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;form-group&quot;&lt;/span&gt;&amp;gt;
  &lt;div class=&quot;divIndent&quot;&gt;
    &amp;lt;BECanvas Width=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;600&quot;&lt;/span&gt; Height=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;129&quot;&lt;/span&gt; &lt;span style=&quot;background-color:#FFFF00&quot;&gt;@&lt;/span&gt;ref=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;_canvasReference&quot;&lt;/span&gt;&amp;gt;&amp;lt;/BECanvas&amp;gt;
  &lt;/div&gt;
  &amp;lt;/div&amp;gt;
&lt;/div&gt;

&lt;br /&gt;
You&#39;ll now add the &lt;i&gt;Select&lt;/i&gt; element that will hold the list of employees. The select element will be given an &lt;i&gt;@onchange&lt;/i&gt; attribute that will call your code&#39;s &lt;i&gt;OnChangeEmployeeList&lt;/i&gt; event handler whenever the user selects an employee.

&lt;br /&gt;&lt;br /&gt;
Because the view will be sending a request to a web service for the list of employees, there&#39;s a chance that the results won&#39;t be available when the view is ready to render. You&#39;ll use an &lt;i&gt;@if&lt;/i&gt; statement to make sure you have data before trying to loop through the list of employees to populate the select element. 

&lt;br /&gt;&lt;br /&gt;
Once the data has been retrieved, Blazor will automatically run your code because the &lt;i&gt;@if&lt;/i&gt; statement will indicate that there&#39;s data. At that point, your code will use an &lt;i&gt;@foreach&lt;/i&gt; loop to iterate over the employee data adding an &lt;i&gt;Option&lt;/i&gt; element for each employee.

&lt;br /&gt;&lt;br /&gt;
The Timesheet API uses pagination so it&#39;s possible that there will be multiple pages of employees. If there are additional pages of data, the &lt;i&gt;NextPageURI&lt;/i&gt; property will hold a value that isn&#39;t &lt;i&gt;&#39;N/A&#39;&lt;/i&gt;. If a page other than the first page is requested, the &lt;i&gt;PrevPageURI&lt;/i&gt; property will hold a value that isn&#39;t &lt;i&gt;&#39;N/A&#39;&lt;/i&gt;. These properties might also be &lt;i&gt;null&lt;/i&gt; if the &lt;i&gt;Employees/Me&lt;/i&gt; endpoint was used because the logged in user doesn&#39;t have security access to the &lt;i&gt;Employees&lt;/i&gt; endpoint.

&lt;br /&gt;&lt;br /&gt;
Rather than display buttons that may never be needed, their HTML will be surrounded by an &lt;i&gt;@if&lt;/i&gt; statement so that the buttons are only rendered if needed.

&lt;br /&gt;&lt;br /&gt;
An &lt;i&gt;@onclick&lt;/i&gt; attribute will be defined for each button that will call your code if the user clicks the button.

&lt;br /&gt;&lt;br /&gt;
Add the code in the following snippet, after the div for your &lt;i&gt;BECanvas&lt;/i&gt; element, in your &lt;i&gt;Index.razor&lt;/i&gt; file:
  
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &amp;lt;div class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;form-group&quot;&lt;/span&gt;&amp;gt;
  &lt;div class=&quot;divIndent&quot;&gt;
    &amp;lt;label for=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;employeeList&quot;&lt;/span&gt;&amp;gt;Employees:&amp;lt;/label&amp;gt;
    &lt;br /&gt;&amp;lt;select id=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;employeeList&quot;&lt;/span&gt; class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;form-control&quot;&lt;/span&gt; size=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;6&quot;&lt;/span&gt;
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;background-color:#FFFF00&quot;&gt;@&lt;/span&gt;onchange=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;OnChangeEmployeeList&quot;&lt;/span&gt;&amp;gt;
    
    &lt;div class=&quot;divIndent&quot;&gt;
      &lt;span style=&quot;background-color:#FFFF00&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (EmployeeResponse != &lt;span style=&quot;color:blue;&quot;&gt;null&lt;/span&gt;)
      &lt;br /&gt;{
      &lt;div class=&quot;divIndent&quot;&gt;
        &lt;span style=&quot;background-color:#FFFF00&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:blue;&quot;&gt;foreach&lt;/span&gt; (Employee employee &lt;span style=&quot;color:blue;&quot;&gt;in&lt;/span&gt; EmployeeResponse.Employees)
        &lt;br /&gt;{
        &lt;div class=&quot;divIndent&quot;&gt;
          &amp;lt;option class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;p-2&quot;&lt;/span&gt; value=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;&lt;span style=&quot;background-color:#FFFF00&quot;&gt;@&lt;/span&gt;employee.ID&quot;&lt;/span&gt;&amp;gt;&lt;span style=&quot;background-color:#FFFF00&quot;&gt;@&lt;/span&gt;employee.LastName, &lt;span style=&quot;background-color:#FFFF00&quot;&gt;@&lt;/span&gt;employee.FirstName&amp;lt;/option&amp;gt;
        &lt;/div&gt;
        }
      &lt;/div&gt;
      }
    &lt;/div&gt;
    &amp;lt;/select&amp;gt;

    &lt;br /&gt;&lt;br /&gt;
    &amp;lt;div class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;p-2 d-flex justify-content-center&quot;&lt;/span&gt;&amp;gt;
    &lt;div class=&quot;divIndent&quot;&gt;
      &lt;span style=&quot;background-color:#FFFF00&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (IsThereAPreviousPage())
      &lt;br /&gt;{
      &lt;div class=&quot;divIndent&quot;&gt;
        &amp;lt;input type=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;button&quot;&lt;/span&gt; class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;btn btn-primary m-2&quot;&lt;/span&gt; value=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;&amp;lt; Previous Employees&quot;&lt;/span&gt;
        &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;background-color:#FFFF00&quot;&gt;@&lt;/span&gt;onclick=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;OnClickPreviousPage&quot;&lt;/span&gt; /&amp;gt;
      &lt;/div&gt;    
      }
      
      &lt;br /&gt;&lt;br /&gt;
      &lt;span style=&quot;background-color:#FFFF00&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (IsThereANextPage())
      &lt;br /&gt;{
      &lt;div class=&quot;divIndent&quot;&gt;
        &amp;lt;input type=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;button&quot;&lt;/span&gt; class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;btn btn-primary m-2&quot;&lt;/span&gt; value=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;Next Employees &amp;gt;&quot;&lt;/span&gt;
        &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;background-color:#FFFF00&quot;&gt;@&lt;/span&gt;onclick=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;OnClickNextPage&quot;&lt;/span&gt; /&amp;gt;
      &lt;/div&gt;
      }
    &lt;/div&gt;
    &amp;lt;/div&amp;gt;
  &lt;/div&gt;
  &amp;lt;/div&amp;gt;
&lt;/div&gt;

  
  
&lt;br /&gt;&lt;br /&gt;
That&#39;s all there is for the view&#39;s HTML. Your next step is to write the code for the view by including an &lt;i&gt;@code&lt;/i&gt; section.

&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;The @code section&lt;/div&gt;

&lt;br /&gt;
The code in a razor file is included within an &lt;i&gt;@code { }&lt;/i&gt; block. The &lt;i&gt;@code&lt;/i&gt; block for this view will start off with some constants and variables for use by the rest of the code. 

&lt;br /&gt;&lt;br /&gt;
Add the contents of the following code snippet at the end of your &lt;i&gt;Index.razor&lt;/i&gt; file:
  
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;background-color:#FFFF00&quot;&gt;@code {&lt;/span&gt;
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;const string&lt;/span&gt; _RootUri = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;https://api.dovico.com/&quot;&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;const string&lt;/span&gt; API_VERSION = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;7&quot;&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;const string&lt;/span&gt; API_DATE_FORMAT = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;yyyy-MM-dd&quot;&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;const string&lt;/span&gt; URI_NOT_AVAILABLE = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;N/A&quot;&lt;/span&gt;;

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;const int&lt;/span&gt; TIMELINE_HEIGHT = 95; &lt;span style=&quot;color:darkgreen;&quot;&gt;// The blue part of the timeline&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;const int&lt;/span&gt; TIMELINE_BORDER_WIDTH = 2;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;const int&lt;/span&gt; DAY_QUARTER_HEIGHT = 18; &lt;span style=&quot;color:darkgreen;&quot;&gt;// 4 rows with a bit of vertical padding between each&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;const int&lt;/span&gt; DATE_RANGE_DAYS = 30;

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;const string&lt;/span&gt; TIMELINE_BACKGROUND_COLOR = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;rgb(141, 165, 237)&quot;&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;const string&lt;/span&gt; TIMELINE_BORDER_COLOR = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;rgb(112, 146, 190)&quot;&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;const string&lt;/span&gt; NON_WORKING_DAY_BACKGROUND_COLOR = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;rgba(112, 146, 190, 0.3)&quot;&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;const string&lt;/span&gt; TODAY_BACKGROUND_COLOR = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;rgb(188, 199, 229)&quot;&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;const string&lt;/span&gt; TEXT_COLOR = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;black&quot;&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;const string&lt;/span&gt; PROCESSING_TEXT_BACKGROUND_COLOR = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;rgba(0, 0, 0, 0.5)&quot;&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;const string&lt;/span&gt; PROCESSING_TEXT_COLOR = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;white&quot;&lt;/span&gt;;

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;const string&lt;/span&gt; STATUS_COLOR_REJECTED_BORDER = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;rgb(178, 0, 0)&quot;&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;const string&lt;/span&gt; STATUS_COLOR_REJECTED_FILL = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;rgb(255, 76, 76)&quot;&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;const string&lt;/span&gt; STATUS_COLOR_UNSUBMITTED_FILL = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;rgb(64, 64, 64)&quot;&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;const string&lt;/span&gt; STATUS_COLOR_UNSUBMITTED_BORDER = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;rgb(0, 0, 0)&quot;&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;const string&lt;/span&gt; STATUS_COLOR_UNDER_REVIEW_FILL = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;rgb(255, 227, 117)&quot;&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;const string&lt;/span&gt; STATUS_COLOR_UNDER_REVIEW_BORDER = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;rgb(255, 183, 0)&quot;&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;const string&lt;/span&gt; STATUS_COLOR_APPROVED_FILL = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;rgb(85, 193, 141)&quot;&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;const string&lt;/span&gt; STATUS_COLOR_APPROVED_BORDER = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;rgb(85, 147, 141)&quot;&lt;/span&gt;;

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;const string&lt;/span&gt; FONTINFO_ARIAL_9 = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;9px arial&quot;&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;const string&lt;/span&gt; FONTINFO_ARIAL_11 = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;11px arial&quot;&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;const string&lt;/span&gt; FONTINFO_ARIAL_26_BOLD = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;bold 26px arial&quot;&lt;/span&gt;;

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Dates with the time portion zeroed out&lt;/span&gt;
    &lt;br /&gt;DateTime _StartDate = DateTime.Now.Date.AddDays(-DATE_RANGE_DAYS);
    &lt;br /&gt;DateTime _EndDate = DateTime.Now.Date;

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Calculated in the OnAfterRenderAsync method&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; _TextHeightArial9 = 0.0;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; _TextHeightArial11 = 0.0;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; _TextHeightArial26Bold = 0.0;

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Canvas drawing references&lt;/span&gt;
    &lt;br /&gt;Canvas2DContext _context = &lt;span style=&quot;color:blue;&quot;&gt;null&lt;/span&gt;;
    &lt;br /&gt;BECanvasComponent _canvasReference;

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Will hold the data received from the API&lt;/span&gt;
    &lt;br /&gt;EmployeeResponse EmployeeResponse = &lt;span style=&quot;color:blue;&quot;&gt;null&lt;/span&gt;;
    &lt;br /&gt;List&amp;lt;TimeEntry&amp;gt; _TimeEntries = &lt;span style=&quot;color:blue;&quot;&gt;new&lt;/span&gt; List&amp;lt;TimeEntry&amp;gt;();

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;bool&lt;/span&gt; _IndicateProcessing = &lt;span style=&quot;color:blue;&quot;&gt;false&lt;/span&gt;;
  &lt;/div&gt;
  &lt;span style=&quot;background-color:#FFFF00&quot;&gt;}&lt;/span&gt;
&lt;/div&gt;

  
  
&lt;br /&gt;&lt;br /&gt;
With your global variables, objects, and constants defined, your next step is to define the &lt;i&gt;OnInitializedAsync&lt;/i&gt; method.

&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;The OnInitializedAsync method&lt;/div&gt;

&lt;br /&gt;
The &lt;i&gt;OnInitializedAsync&lt;/i&gt; method is one of Blazor&#39;s life cycle methods that&#39;s called after the component has been initialized. The canvas element won&#39;t be available at this point, but you can make requests to the Dovico Timesheet API at this point. The following web page has more information about Blazor&#39;s life cycle methods: &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/blazor/components/lifecycle?view=aspnetcore-3.1&quot; target=&quot;_blank&quot;&gt;https://docs.microsoft.com/en-us/aspnet/core/blazor/components/lifecycle?view=aspnetcore-3.1&lt;/a&gt;
  
&lt;br /&gt;&lt;br /&gt;
You&#39;ll use the &lt;i&gt;OnInitializedAsync&lt;/i&gt; method to call the &lt;i&gt;LoadEmployeesAsync&lt;/i&gt; helper method, that you&#39;ll define in a moment, asking it to request the list of employees from the Dovico Timesheet API. If the call fails with an &lt;i&gt;Unauthorized&lt;/i&gt; status code, your code will call the &lt;i&gt;LoadEmployeesAsync&lt;/i&gt; method again but asking it to call the API&#39;s &lt;i&gt;Employees/Me&lt;/i&gt; endpoint instead.

&lt;br /&gt;&lt;br /&gt;
Add the &lt;i&gt;OnInitializedAsync&lt;/i&gt; method in the following snippet at the end of your &lt;i&gt;Index.razor&lt;/i&gt; file but before the &lt;i&gt;@code&lt;/i&gt;&#39;s closing curly brace:
  
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;protected override async&lt;/span&gt; Task OnInitializedAsync()
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    HttpResponseMessage response = &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; LoadEmployeesAsync(_RootUri + 
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color:maroon;&quot;&gt;&quot;Employees/?version=&quot;&lt;/span&gt; + API_VERSION);
    
    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (!response.IsSuccessStatusCode &amp;amp;&amp;amp;
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
    &lt;br /&gt;{
    &lt;div class=&quot;divIndent&quot;&gt;
      &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; LoadEmployeesAsync(_RootUri + &lt;span style=&quot;color:maroon;&quot;&gt;&quot;Employees/Me/?version=&quot;&lt;/span&gt; + API_VERSION);
    &lt;/div&gt;
    }
  &lt;/div&gt;
  }
&lt;/div&gt;

  
  
&lt;br /&gt;&lt;br /&gt;
The next method you need to define is the &lt;i&gt;LoadEmployeesAsync&lt;/i&gt; method.

&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;The LoadEmployeesAsync method&lt;/div&gt;

&lt;br /&gt;
The &lt;i&gt;LoadEmployeesAsync&lt;/i&gt; method first sets the &lt;i&gt;EmployeeResponse&lt;/i&gt; object to &lt;i&gt;null&lt;/i&gt; so that the view&#39;s employees element is cleared. It then flags that processing is happening by setting the &lt;i&gt;_IndicateProcessing&lt;/i&gt; global variable to &lt;i&gt;true&lt;/i&gt;. Then, it calls the &lt;i&gt;DrawTimelineAsync&lt;/i&gt; method to draw the timeline with the processing text overlaid.

&lt;br /&gt;&lt;br /&gt;
If the &lt;i&gt;LoadEmployeesAsync&lt;/i&gt; method is called by the &lt;i&gt;OnInitializedAsync&lt;/i&gt;, the canvas won&#39;t be ready yet so the call to &lt;i&gt;DrawTimelineAsync&lt;/i&gt; won&#39;t actually draw anything. The canvas will only become available to your code in the &lt;i&gt;OnAfterRenderAsync&lt;/i&gt; method that you&#39;ll see shortly. The canvas will likely be ready if this method is called when the user clicks on the previous or next page button. 

&lt;br /&gt;&lt;br /&gt;
Because the processing flag (&lt;i&gt;_IndicateProcessing&lt;/i&gt;) is set to &lt;i&gt;true&lt;/i&gt;, if the &lt;i&gt;OnAfterRenderAsync&lt;/i&gt; method is called while this method is still processing, it will also call the &lt;i&gt;DrawTimelineAsync&lt;/i&gt; method which will show the processing text at that point.

&lt;br /&gt;&lt;br /&gt;
After calling the DrawTimelineAsync method to indicate processing, this method will then request the data from the API endpoint specified in the &lt;i&gt;uri&lt;/i&gt; parameter. If the HTTP response indicates success, the JSON string is parsed and placed in the &lt;i&gt;EmployeeResponse&lt;/i&gt; object which will cause the view&#39;s employee list to be populated automatically by Blazor because the object is no longer &lt;i&gt;null&lt;/i&gt;.

&lt;br /&gt;&lt;br /&gt;
Finally, this method ends by flagging that processing is no longer occurring and then returns the &lt;i&gt;HttpResponseMessage&lt;/i&gt; object to the calling method so that it can check to see if the API call was successful and what the status code was.

&lt;br /&gt;&lt;br /&gt;
Add the code in the following snippet to the end of your &lt;i&gt;Index.razor&lt;/i&gt; file but before the &lt;i&gt;@code&lt;/i&gt;&#39;s closing curly brace:

&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;protected async&lt;/span&gt; Task&amp;lt;HttpResponseMessage&amp;gt; LoadEmployeesAsync(&lt;span style=&quot;color:blue;&quot;&gt;string&lt;/span&gt; uri)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    EmployeeResponse = &lt;span style=&quot;color:blue;&quot;&gt;null&lt;/span&gt;;

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Flag that the view is processing and draw the timeline. Don&#39;t await DrawTimelineAsync.&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// Request the list of employees while the timeline refreshes and shows the processing&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// indicator.&lt;/span&gt;
    &lt;br /&gt;_IndicateProcessing = &lt;span style=&quot;color:blue;&quot;&gt;true&lt;/span&gt;;
    &lt;br /&gt;DrawTimelineAsync(&lt;span style=&quot;color:blue;&quot;&gt;null&lt;/span&gt;);

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Load the requested page of employees. If the call was successful then parse the JSON.&lt;/span&gt;
    &lt;br /&gt;HttpResponseMessage response = &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; Http.GetAsync(uri);
    &lt;br /&gt;if (response.IsSuccessStatusCode)
    &lt;br /&gt;{
    &lt;div class=&quot;divIndent&quot;&gt;
      EmployeeResponse =
      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; response.Content.ReadFromJsonAsync&amp;lt;EmployeeResponse&amp;gt;();
    &lt;/div&gt;
    }

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Now that you have the list of employees, turn off the processing flag. Again, don&#39;t wait&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// for the draw to finish.&lt;/span&gt;
    &lt;br /&gt;_IndicateProcessing = &lt;span style=&quot;color:blue;&quot;&gt;false&lt;/span&gt;;
    &lt;br /&gt;DrawTimelineAsync(&lt;span style=&quot;color:blue;&quot;&gt;null&lt;/span&gt;);

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;return&lt;/span&gt; response;
  &lt;/div&gt;
  }
&lt;/div&gt;

 

&lt;br /&gt;&lt;br /&gt;
The next methods that you need to define are for your previous and next page buttons.

&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;The paging methods&lt;/div&gt;

&lt;br /&gt;
The first paging methods needed are &lt;i&gt;IsThereAPrevousPage&lt;/i&gt; and &lt;i&gt;IsThereANextPage&lt;/i&gt;. These methods are used by the &lt;i&gt;@if&lt;/i&gt; statement in the HTML to determine if the previous or next buttons should be rendered. If the &lt;i&gt;EmployeeResponse&lt;/i&gt; object&#39;s &lt;i&gt;PrevPageURI&lt;/i&gt; property is &lt;i&gt;null&lt;/i&gt; or &lt;i&gt;&#39;N/A&#39;&lt;/i&gt; then the previous page button won&#39;t be displayed. Likewise, if the &lt;i&gt;NextPageURI&lt;/i&gt; property is &lt;i&gt;null&lt;/i&gt; or &lt;i&gt;&#39;N/A&#39;&lt;/i&gt; then the next page button won&#39;t be displayed.

&lt;br /&gt;&lt;br /&gt;
The next two paging methods that you need to define are the &lt;i&gt;OnClickPreviousPage&lt;/i&gt; and &lt;i&gt;OnClickNextPage&lt;/i&gt; event handlers. These methods are called when the previous or next button, respectively, is clicked on. When the button is clicked, the method will pass the &lt;i&gt;PreviousPageURI&lt;/i&gt; or &lt;i&gt;NextPageURI&lt;/i&gt; value to the &lt;i&gt;LoadEmployeesAsync&lt;/i&gt; method to have that page of employee data pulled from the Dovico Timesheet API.

&lt;br /&gt;&lt;br /&gt;
Add the code in the following snippet before the &lt;i&gt;@code&lt;/i&gt;&#39;s closing curly brace at the end of your &lt;i&gt;Index.razor&lt;/i&gt; file:
  
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;protected bool&lt;/span&gt; IsThereAPreviousPage()
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;return&lt;/span&gt; (EmployeeResponse != &lt;span style=&quot;color:blue;&quot;&gt;null&lt;/span&gt; &amp;amp;&amp;amp; EmployeeResponse.PrevPageURI != &lt;span style=&quot;color:blue;&quot;&gt;null&lt;/span&gt;
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;&amp;amp; EmployeeResponse.PrevPageURI != URI_NOT_AVAILABLE);
  &lt;/div&gt;
  }
  &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;protected bool&lt;/span&gt; IsThereANextPage()
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;return&lt;/span&gt; (EmployeeResponse != &lt;span style=&quot;color:blue;&quot;&gt;null&lt;/span&gt; &amp;amp;&amp;amp; EmployeeResponse.NextPageURI != &lt;span style=&quot;color:blue;&quot;&gt;null&lt;/span&gt;
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;&amp;amp; EmployeeResponse.NextPageURI != URI_NOT_AVAILABLE);
  &lt;/div&gt;
  }

  &lt;br /&gt;&lt;br /&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;protected async&lt;/span&gt; Task OnClickPreviousPage(MouseEventArgs args)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; LoadEmployeesAsync(EmployeeResponse.PrevPageURI);
  &lt;/div&gt;
  }
  &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;protected async&lt;/span&gt; Task OnClickNextPage(MouseEventArgs args)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; LoadEmployeesAsync(EmployeeResponse.NextPageURI);
  &lt;/div&gt;
  }
&lt;/div&gt;

  
  
&lt;br /&gt;&lt;br /&gt;
The next method you need to define is the &lt;i&gt;OnChangeEmployeeList&lt;/i&gt; method.

&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;The OnChangeEmployeeList method&lt;/div&gt;

&lt;br /&gt;
The &lt;i&gt;OnChangeEmployeeList&lt;/i&gt; method is called when the user clicks on an employee in the employee list. Your method will indicate that the view is processing so that the timeline displays the processing text. Then it will call the &lt;i&gt;LoadTimeEntriesAsync&lt;/i&gt; method passing in a URI requesting the selected employee&#39;s time entries over the past 30 days.

&lt;br /&gt;&lt;br /&gt;
The list of time entries will be returned by the API grouped by unapproved time and then approved time. Your method will sort the received list of time entries by date instead.

&lt;br /&gt;&lt;br /&gt;
The method will then indicate that processing is over and will call the &lt;i&gt;DrawTimelineAsync&lt;/i&gt; method passing in the employee object that belongs to the selected employee. This will cause the timeline to draw the selected employee&#39;s time entry statuses.

&lt;br /&gt;&lt;br /&gt;
Add the code in the following snippet to the end of your &lt;i&gt;Index.razor&lt;/i&gt; file before the &lt;i&gt;@code&lt;/i&gt;&#39;s closing curly brace:
  
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;protected async&lt;/span&gt; Task OnChangeEmployeeList(ChangeEventArgs e)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Flag that the view is processing and draw the timeline. Don&#39;t await DrawTimelineAsync.&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// Request the list of time entries while the timeline refreshes and shows the processing&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// indicator.&lt;/span&gt;
    &lt;br /&gt;_IndicateProcessing = &lt;span style=&quot;color:blue;&quot;&gt;true&lt;/span&gt;;
    &lt;br /&gt;DrawTimelineAsync(&lt;span style=&quot;color:blue;&quot;&gt;null&lt;/span&gt;);

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;string&lt;/span&gt; EmployeeID = e.Value.ToString();

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Clear the list of time entries just in case there are some from another employee. Load in&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// the selected employee&#39;s time for the date range.&lt;/span&gt;
    &lt;br /&gt;_TimeEntries.Clear();
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; LoadTimeEntriesAsync(_RootUri + &lt;span style=&quot;color:maroon;&quot;&gt;&quot;TimeEntries/Employee/&quot;&lt;/span&gt; + EmployeeID + 
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color:maroon;&quot;&gt;&quot;/?version=&quot;&lt;/span&gt; + API_VERSION + &lt;span style=&quot;color:maroon;&quot;&gt;&quot;&amp;amp;daterange=&quot;&lt;/span&gt; + 
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_StartDate.ToString(API_DATE_FORMAT) + &lt;span style=&quot;color:maroon;&quot;&gt;&quot;%20&quot;&lt;/span&gt; + 
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_EndDate.ToString(API_DATE_FORMAT));

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Sort the time entries by date. They were returned grouped by unapproved and then&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// approved.&lt;/span&gt;
    &lt;br /&gt;_TimeEntries.Sort((a, b) =&amp;gt; a.TheDate.CompareTo(b.TheDate));

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Turn off the processing flag&lt;/span&gt;
    &lt;br /&gt;_IndicateProcessing = &lt;span style=&quot;color:blue;&quot;&gt;false&lt;/span&gt;;

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Find the employee object belonging to the selected employee id. Redraw the timeline&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// with the employee&#39;s time.&lt;/span&gt;
    &lt;br /&gt;Employee Employee = EmployeeResponse.Employees.Find(item =&amp;gt; item.ID == EmployeeID);
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawTimelineAsync(Employee);
  &lt;/div&gt;
  }
&lt;/div&gt;
  
  
  
&lt;br /&gt;&lt;br /&gt;
The next method to define is the &lt;i&gt;LoadTimeEntriesAsync&lt;/i&gt; method.

&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;The LoadTimeEntriesAsync method&lt;/div&gt;

&lt;br /&gt;
The &lt;i&gt;LoadTimeEntriesAsync&lt;/i&gt; method requests the time entries from the Dovico Timesheet API that are within the date range specified. If time entries are returned, they will be added to the &lt;i&gt;_TimeEntries&lt;/i&gt; global variable. 

&lt;br /&gt;&lt;br /&gt;
Because time entry data is returned from the Dovico Timesheet API using pagination, it&#39;s possible that there are multiple pages of time that need to be requested. If there&#39;s a next page of time data available, this method will call itself again passing in the &lt;i&gt;NextPageURI&lt;/i&gt; to pull the next page of data.

&lt;br /&gt;&lt;br /&gt;
Add the code in the following snippet to the end of your &lt;i&gt;Index.razor&lt;/i&gt; file before the &lt;i&gt;@code&lt;/i&gt;&#39;s closing curly brace:
  
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;protected async&lt;/span&gt; Task LoadTimeEntriesAsync(&lt;span style=&quot;color:blue;&quot;&gt;string&lt;/span&gt; uri)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// If time entries were returned, add them to the list.&lt;/span&gt;
    &lt;br /&gt;TimeEntryResponse response = &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; Http.GetFromJsonAsync&amp;lt;TimeEntryResponse&amp;gt;(uri);
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (response.TimeEntries.Count &amp;gt; 0) { _TimeEntries.AddRange(response.TimeEntries); }

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// If there&#39;s a next page of time entries, call this method again passing in the NextPageURI&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (response.NextPageURI != URI_NOT_AVAILABLE)
    &lt;br /&gt;{
    &lt;div class=&quot;divIndent&quot;&gt;
      &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; LoadTimeEntriesAsync(response.NextPageURI); 
    &lt;/div&gt;
    }
  &lt;/div&gt;
  }
&lt;/div&gt;
  
  

&lt;br /&gt;&lt;br /&gt;
The next method to define is the &lt;i&gt;OnAfterRenderAsync&lt;/i&gt; method.

&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;The OnAfterRenderAsync method&lt;/div&gt;

&lt;br /&gt;
The &lt;i&gt;OnAfterRenderAsync&lt;/i&gt; method is another Blazor lifecycle method that you need to implement because the HTML Canvas element only becomes available to your code at this point. 

&lt;br /&gt;&lt;br /&gt;
This method gets called when the page initially renders but also when a selection is changed in the employee list and when the canvas is drawn. The code in this method only needs to run once so you&#39;ll check the &lt;i&gt;FirstRender&lt;/i&gt; parameter to see if it&#39;s &lt;i&gt;true&lt;/i&gt;.

&lt;br /&gt;&lt;br /&gt;
This method will get the Canvas element&#39;s 2D context, calculate some font heights, and will then draw the initial timeline.

&lt;br /&gt;&lt;br /&gt;
I&#39;m not going to go into detail about the drawing calls used for a canvas element in this article but, if you&#39;re interested, the following web page has an excellent tutorial: &lt;a href=&quot;http://diveintohtml5.info/canvas.html&quot; target=&quot;_blank&quot;&gt;http://diveintohtml5.info/canvas.html&lt;/a&gt;

&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divIndentBothSides&quot;&gt;
  &lt;b&gt;NOTE:&lt;/b&gt; The tutorial interacts with the canvas context directly whereas, here, the Blazor.Extensions.Canvas objects are wrapping those calls. From what I can tell, here, all the methods are named the same but with the word &lt;i&gt;Async&lt;/i&gt; on the end.
&lt;/div&gt;

&lt;br /&gt;
Add the code in the following snippet before the &lt;i&gt;@code&lt;/i&gt;&#39;s closing curly brace at the end of your &lt;i&gt;Index.razor&lt;/i&gt; file:
  
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;protected override async&lt;/span&gt; Task OnAfterRenderAsync(&lt;span style=&quot;color:blue;&quot;&gt;bool&lt;/span&gt; FirstRender)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// This method gets run multiple times but the following code only needs to run once&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (FirstRender)
    &lt;br /&gt;{
    &lt;div class=&quot;divIndent&quot;&gt;
      &lt;span style=&quot;color:darkgreen;&quot;&gt;// Get the 2D context from the canvas&lt;/span&gt;
      &lt;br /&gt;_context = &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _canvasReference.CreateCanvas2DAsync();

      &lt;br /&gt;&lt;br /&gt;
      &lt;span style=&quot;color:darkgreen;&quot;&gt;// Rather than calculating these values every time the DrawTimelineAsync method is&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// called, cache the values&lt;/span&gt;
      &lt;br /&gt;_TextHeightArial9 = &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; GetTextHeightAsync(FONTINFO_ARIAL_9);
      &lt;br /&gt;_TextHeightArial11 = &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; GetTextHeightAsync(FONTINFO_ARIAL_11);
      &lt;br /&gt;_TextHeightArial26Bold = &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; GetTextHeightAsync(FONTINFO_ARIAL_26_BOLD);

      &lt;br /&gt;&lt;br /&gt;
      &lt;span style=&quot;color:darkgreen;&quot;&gt;// Straddle the pixels to prevent blurry horizontal and vertical lines&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// (http://diveintohtml5.info/canvas.html).&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.TranslateAsync(0.5, 0.5);

      &lt;br /&gt;&lt;br /&gt;
      &lt;span style=&quot;color:darkgreen;&quot;&gt;// There&#39;s no employee selected at this point. Just draw the timeline itself.&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawTimelineAsync(&lt;span style=&quot;color:blue;&quot;&gt;null&lt;/span&gt;);
    &lt;/div&gt;
    }
  &lt;/div&gt;
  }
&lt;/div&gt;
  
  

&lt;br /&gt;&lt;br /&gt;
The next method to define is the &lt;i&gt;DrawTimelineAsync&lt;/i&gt; method.

&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;The DrawTimelineAsync method&lt;/div&gt;

&lt;br /&gt;
The &lt;i&gt;DrawTimelineAsync&lt;/i&gt; method is responsible for drawing the timeline if the canvas&#39; 2D context has been obtained. If the context hasn&#39;t yet been obtained, there&#39;s no way to draw the canvas so the method exits.

&lt;br /&gt;&lt;br /&gt;
The method starts off by making some calculations like where the top of the blue background will start to give room for the month&#39;s abbreviation above the timeline. It will determine how wide each day can be based on the width of the canvas and the 31 days of time that will be displayed.

&lt;br /&gt;&lt;br /&gt;
One thing to note is that, although your code is written in C#, the Canvas element is an HTML element on the web page and your C# code is running in the mono runtime. The mono runtime is compiled to WebAssembly and running side-by-side with the JavaScript of the web page but there&#39;s a penalty for switching contexts. Depending on how many calls you make, and depending on the browser, this may be noticeable. 

&lt;br /&gt;&lt;br /&gt;
When dealing with the canvas, one thing you can do is batch your calls by first calling the context&#39;s &lt;i&gt;BeginBatchAsync&lt;/i&gt; method and then the &lt;i&gt;EndBatchAsync&lt;/i&gt; method when you&#39;ve finished specifying all the calls. I&#39;ve noticed that the &lt;i&gt;MeasureTextAsync&lt;/i&gt; method doesn&#39;t work when you use the BeginBatchAsync and EndBatchAsync methods but you can still use the methods to batch some of the calls where the MeasureTextAsync method isn&#39;t used.

&lt;br /&gt;&lt;br /&gt;
Another thing to be aware of is that the mono runtime and your code are running in the same thread as the UI. It&#39;s good practice to make all methods asynchronous if they do processing so that your web page doesn&#39;t become unresponsive.

&lt;br /&gt;&lt;br /&gt;
Add the code in the following snippet before the &lt;i&gt;@code&lt;/i&gt;&#39;s closing curly brace at the end of your &lt;i&gt;Index.razor&lt;/i&gt; file:
  
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;protected async&lt;/span&gt; Task DrawTimelineAsync(Employee Employee)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Exit if the Canvas isn&#39;t available&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (_context == &lt;span style=&quot;color:blue;&quot;&gt;null&lt;/span&gt;) { &lt;span style=&quot;color:blue;&quot;&gt;return&lt;/span&gt;; }

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Returned as long but it impacts the DayCellWidth calculation if left as a long&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; CanvasHeight = _canvasReference.Height;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; CanvasWidth = _canvasReference.Width;

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Give a bit of space before the top of the timeline for the month abbreviation(s)&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; TimelineTop = (_TextHeightArial11 + 5);

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Day (cell) calculations (Date Range is 30 days before today&#39;s date which is 31 days in&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// total so add 1 to the days)&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; DayCellWidth = ((CanvasWidth - TIMELINE_BORDER_WIDTH) /
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(DATE_RANGE_DAYS + 1));
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; HalfDayCellWidth = (DayCellWidth / 2);
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; DayTop = (TimelineTop + DAY_QUARTER_HEIGHT);

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Clear the previous drawing. Need to start at -0.5 because of the call to TranslateAsync&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// in the OnAfterRenderAsync method to straddle the pixels so that a 1 pixel line is actually&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// 1 pixel wide.&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.ClearRectAsync(-0.5, -0.5, CanvasWidth, CanvasHeight);

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Fill the timeline&#39;s background with the blue&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.SetFillStyleAsync(TIMELINE_BACKGROUND_COLOR);
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.FillRectAsync(0, TimelineTop, CanvasWidth, TIMELINE_HEIGHT);

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Draw the border&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.BeginPathAsync(); &lt;span style=&quot;color:darkgreen;&quot;&gt;// Clears previous lines that were drawn&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.SetLineWidthAsync(TIMELINE_BORDER_WIDTH);
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.SetStrokeStyleAsync(TIMELINE_BORDER_COLOR);
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.StrokeRectAsync(1, TimelineTop,
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(CanvasWidth - TIMELINE_BORDER_WIDTH - 1), TIMELINE_HEIGHT);

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Set the stroke to a width of 1 for the rest of the lines&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.SetLineWidthAsync(1);

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt; CurrentDayIndex = 0;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; MonthRightX = 0.0;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; X = 0.0;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; Y = 0.0;

    &lt;br /&gt;&lt;br /&gt;
    DateTime LoopDate = _StartDate;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;while&lt;/span&gt; (LoopDate &amp;lt;= _EndDate)
    &lt;br /&gt;{
    &lt;div class=&quot;divIndent&quot;&gt;
      &lt;span style=&quot;color:darkgreen;&quot;&gt;// Draw the current month&#39;s abbreviation above the timeline if this is the first time&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// through the loop.&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (CurrentDayIndex == 0)
      &lt;br /&gt;{
      &lt;div class=&quot;divIndent&quot;&gt;
        MonthRightX = &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawMonthAbbreviationAsync((X + 4), (TimelineTop - 5),
        &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LoopDate); 
      &lt;/div&gt;
      }

      &lt;br /&gt;&lt;br /&gt;
      &lt;span style=&quot;color:darkgreen;&quot;&gt;// If the current day is the first day of the month and this is not the first time through the&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// loop...(if this is the first time through the loop, the month abbreviation was already&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// drawn above)&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (LoopDate.Day == 1 &amp;amp;&amp;amp; CurrentDayIndex &amp;gt; 0)
      &lt;br /&gt;{
      &lt;div class=&quot;divIndent&quot;&gt;
        &lt;span style=&quot;color:darkgreen;&quot;&gt;// Draw a vertical line separating the previous month from the new one&lt;/span&gt;
        &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.BeginPathAsync();
        &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.SetStrokeStyleAsync(TIMELINE_BORDER_COLOR);
        &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.MoveToAsync(X, TimelineTop);
        &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.LineToAsync(X, (TimelineTop + TIMELINE_HEIGHT));
        &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.StrokeAsync();

        &lt;br /&gt;&lt;br /&gt;
        &lt;span style=&quot;color:darkgreen;&quot;&gt;// Determine the X position of the text. If the position will overlap an existing&lt;/span&gt;
        &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// abbreviation then adjust the X position to accommodate the previous abbreviation.&lt;/span&gt;
        &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; MonthX = (X + 4);
        &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; ((X + 4) &amp;lt;= (MonthRightX + 4)) { MonthX = (MonthRightX + 4); }

        &lt;br /&gt;&lt;br /&gt;
        &lt;span style=&quot;color:darkgreen;&quot;&gt;// Draw the current month&#39;s abbreviation&lt;/span&gt;
        &lt;br /&gt;MonthRightX = &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawMonthAbbreviationAsync(MonthX, (TimelineTop - 5),
        &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LoopDate);
      &lt;/div&gt;
      }

      &lt;br /&gt;&lt;br /&gt;
      &lt;span style=&quot;color:darkgreen;&quot;&gt;// If this is the last date of the loop...&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (LoopDate == _EndDate)
      &lt;br /&gt;{
      &lt;div class=&quot;divIndent&quot;&gt;
        &lt;span style=&quot;color:darkgreen;&quot;&gt;// Draw the background differently to signal this is the current date&lt;/span&gt;
        &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.SetStrokeStyleAsync(TIMELINE_BORDER_COLOR);
        &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.SetFillStyleAsync(TODAY_BACKGROUND_COLOR);
        &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.FillRectAsync(X, TimelineTop, DayCellWidth, TIMELINE_HEIGHT);
        &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.StrokeRectAsync(X, TimelineTop, DayCellWidth, TIMELINE_HEIGHT);
      &lt;/div&gt;
      }
      &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;else&lt;/span&gt; &lt;span style=&quot;color:darkgreen;&quot;&gt;// Haven&#39;t yet reached today&#39;s date...&lt;/span&gt;
      &lt;br /&gt;{
      &lt;div class=&quot;divIndent&quot;&gt;
        &lt;span style=&quot;color:darkgreen;&quot;&gt;// If this is a non-working day, draw the background for a non-working day&lt;/span&gt;
        &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawNonWorkingDayBackgroundAsync(X, TimelineTop, DayCellWidth,
        &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;TIMELINE_HEIGHT, LoopDate, Employee);
      &lt;/div&gt;
      }

      &lt;br /&gt;&lt;br /&gt;
      &lt;span style=&quot;color:darkgreen;&quot;&gt;// Draw the day of the month (e.g. 1, 2, ...30, 31) just below the top border&lt;/span&gt;
      &lt;br /&gt;Y = (TimelineTop + TIMELINE_BORDER_WIDTH + _TextHeightArial9 + 2);
      &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawDayOfMonthAsync((X + HalfDayCellWidth), Y, LoopDate);

      &lt;br /&gt;&lt;br /&gt;
      &lt;span style=&quot;color:darkgreen;&quot;&gt;// If an employee is selected...&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (Employee != &lt;span style=&quot;color:blue;&quot;&gt;null&lt;/span&gt;)
      &lt;br /&gt;{
      &lt;div class=&quot;divIndent&quot;&gt;
        &lt;span style=&quot;color:darkgreen;&quot;&gt;// Draw the Status indicators (indicates if there are Rejected, Unsubmitted, Under&lt;/span&gt;
        &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// Review, or Approved time entries for the current date)&lt;/span&gt;
        &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawStatusesAsync(X, DayTop, DayCellWidth, LoopDate);
      &lt;/div&gt;
      }

      &lt;br /&gt;&lt;br /&gt;
      &lt;span style=&quot;color:darkgreen;&quot;&gt;// Increment the current index and date for the next loop&lt;/span&gt;
      &lt;br /&gt;LoopDate = LoopDate.AddDays(1);
      &lt;br /&gt;CurrentDayIndex++;

      &lt;br /&gt;&lt;br /&gt;
      &lt;span style=&quot;color:darkgreen;&quot;&gt;// Determine the X position for the next day&#39;s data&lt;/span&gt;
      &lt;br /&gt;X = (CurrentDayIndex * DayCellWidth);
    &lt;/div&gt;
    }

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Draw the legend under the timeline (+ 2 so there&#39;s a bit of padding between the bottom&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// of the timeline and top of the triangles)&lt;/span&gt;
    &lt;br /&gt;Y = (TimelineTop + TIMELINE_HEIGHT + 2);
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawLegendAsync(4, Y);

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Draw Today&#39;s date (e.g. &#39;Today (July 15, 2020)&#39;) below the timeline&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawTodayStringAsync((X - TIMELINE_BORDER_WIDTH), (Y + _TextHeightArial11),
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_EndDate);

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Draw the &#39;Processing...&#39; text if the view flagged that it&#39;s processing&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawProcessingAsync(CanvasWidth, CanvasHeight);
  &lt;/div&gt;
  }
&lt;/div&gt;
  
  

&lt;br /&gt;&lt;br /&gt;
The next method to define is the &lt;i&gt;GetTextHeightAsync&lt;/i&gt; method.

&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;The GetTextHeightAsync method&lt;/div&gt;

&lt;br /&gt;
At the moment, the canvas method &lt;i&gt;MeasureTextAsync&lt;/i&gt; only returns a &lt;i&gt;Width&lt;/i&gt; value with the rest of the properties set to &lt;i&gt;zero&lt;/i&gt;. As a result, the &lt;i&gt;GetTextHeightAsync&lt;/i&gt; method has been created to return the approximate height of the text based on the font information specified. Measuring the width of an &lt;i&gt;M&lt;/i&gt; character returns a width that is close to the height of the text.

&lt;br /&gt;&lt;br /&gt;
Add the code in the following snippet to the end of your &lt;i&gt;Index.razor&lt;/i&gt; file before the &lt;i&gt;@code&lt;/i&gt;&#39;s closing curly brace:
  
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;protected async&lt;/span&gt; Task&amp;lt;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt;&amp;gt; GetTextHeightAsync(&lt;span style=&quot;color:blue;&quot;&gt;string&lt;/span&gt; FontInfo)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.SetFontAsync(FontInfo);
    &lt;br /&gt;TextMetrics Metrics = &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.MeasureTextAsync(&lt;span style=&quot;color:maroon;&quot;&gt;&quot;M&quot;&lt;/span&gt;);
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;return&lt;/span&gt; Metrics.Width;
  &lt;/div&gt;
  }
&lt;/div&gt;
  
  

&lt;br /&gt;&lt;br /&gt;
The next method to define is the &lt;i&gt;DrawNonWorkingDayBackgroundAsync&lt;/i&gt; method.

&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;The DrawNonWorkingDayBackgroundAsync method&lt;/div&gt;

&lt;br /&gt;
The &lt;i&gt;DrawNonWorkingDayBackgroundAsync&lt;/i&gt; method draws a slightly darker background on days that are non-working days for the selected employee. If there isn&#39;t an employee selected, the default non-working days are &lt;i&gt;Saturday&lt;/i&gt; and &lt;i&gt;Sunday&lt;/i&gt;.

&lt;br /&gt;&lt;br /&gt;
Add the code in the following snippet, before the &lt;i&gt;@code&lt;/i&gt;&#39;s closing curly brace, at the end of your &lt;i&gt;Index.razor&lt;/i&gt; file:

&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;protected async&lt;/span&gt; Task DrawNonWorkingDayBackgroundAsync(&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; X, &lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; Y,
  &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; Width, &lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; Height, DateTime LoopDate, Employee Employee)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;bool&lt;/span&gt; IsNonWorkingDay = &lt;span style=&quot;color:blue;&quot;&gt;false&lt;/span&gt;;

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// If no employee is selected...&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (Employee == &lt;span style=&quot;color:blue;&quot;&gt;null&lt;/span&gt;)
    &lt;br /&gt;{
    &lt;div class=&quot;divIndent&quot;&gt;
      &lt;span style=&quot;color:darkgreen;&quot;&gt;// Saturday and Sunday are the default non-working days&lt;/span&gt;
      &lt;br /&gt;IsNonWorkingDay = (LoopDate.DayOfWeek == DayOfWeek.Saturday ||
      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LoopDate.DayOfWeek == DayOfWeek.Sunday);
    &lt;/div&gt;
    }
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;else&lt;/span&gt; &lt;span style=&quot;color:darkgreen;&quot;&gt;// There is an employee selected...&lt;/span&gt;
    &lt;br /&gt;{
    &lt;div class=&quot;divIndent&quot;&gt;
      &lt;span style=&quot;color:darkgreen;&quot;&gt;// Check to see if this day is a non-working day for the selected employee&lt;/span&gt;
      &lt;br /&gt;IsNonWorkingDay = !Employee.IsWorkingDay(LoopDate.DayOfWeek);
    &lt;/div&gt;
    }

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// If this is a non-working day...&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (IsNonWorkingDay)
    &lt;br /&gt;{
    &lt;div class=&quot;divIndent&quot;&gt;
      &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.SetFillStyleAsync(NON_WORKING_DAY_BACKGROUND_COLOR);
      &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.FillRectAsync(X, Y, Width, Height);
    &lt;/div&gt;
    }
  &lt;/div&gt;
  }
&lt;/div&gt;
  
  

&lt;br /&gt;&lt;br /&gt;
The next methods to define are the &lt;i&gt;DrawMonthAbbreviationAsync&lt;/i&gt;, &lt;i&gt;DrawDayOfMonthAsync&lt;/i&gt;, &lt;i&gt;DrawTodayStringAsync&lt;/i&gt; and methods.

&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;The date text drawing methods&lt;/div&gt;

&lt;br /&gt;
The first method is the &lt;i&gt;DrawMonthAbbreviationAsync&lt;/i&gt; method that draws the month&#39;s abbreviation (&lt;i&gt;Sep&lt;/i&gt; for &lt;i&gt;September&lt;/i&gt; for example) at the location specified. The method also returns the right edge of the text that was drawn so that the timeline doesn&#39;t draw over the previous abbreviation if the month changes early on the timeline.

&lt;br /&gt;&lt;br /&gt;
The second method is the &lt;i&gt;DrawDayOfMonthAsync&lt;/i&gt; method that draws the date&#39;s day of the month value (&lt;i&gt;28&lt;/i&gt;, &lt;i&gt;29&lt;/i&gt;, &lt;i&gt;30&lt;/i&gt; for example) along the top of the timeline.

&lt;br /&gt;&lt;br /&gt;
The third method is the &lt;i&gt;DrawTodayStringAsync&lt;/i&gt; method that draws the current date under the timeline at the right edge of the canvas to indicate the last date displayed on the timeline.

&lt;br /&gt;&lt;br /&gt;
Add the code in the following snippet, before the &lt;i&gt;@code&lt;/i&gt;&#39;s closing curly brace, at the end of your &lt;i&gt;Index.razor&lt;/i&gt; file:
  
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;protected async&lt;/span&gt; Task&amp;lt;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt;&amp;gt; DrawMonthAbbreviationAsync(&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; X, &lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; Y,
  &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DateTime LoopDate)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;string&lt;/span&gt; Text = LoopDate.ToString(&lt;span style=&quot;color:maroon;&quot;&gt;&quot;MMM&quot;&lt;/span&gt;);
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; Width = &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawTextAsync(X, Y, Text, FONTINFO_ARIAL_11);

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;return&lt;/span&gt; (X + Width);
  &lt;/div&gt;
  }

  &lt;br /&gt;&lt;br /&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;protected async&lt;/span&gt; Task DrawDayOfMonthAsync(&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; X, &lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; Y, DateTime LoopDate)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Text is horizontally centered in the day&#39;s column&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;string&lt;/span&gt; Text = LoopDate.Day.ToString(); &lt;span style=&quot;color:darkgreen;&quot;&gt;// The numeric day value (e.g. 1, 2, ... 30, 31)&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawTextAsync(X, Y, Text, FONTINFO_ARIAL_9, TEXT_COLOR, TextAlign.Center);
  &lt;/div&gt;
  }

  &lt;br /&gt;&lt;br /&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;protected async&lt;/span&gt; Task DrawTodayStringAsync(&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; X, &lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; Y, DateTime Today)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Text is right aligned on the X&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;string&lt;/span&gt; Text = Today.ToString(&lt;span style=&quot;color:maroon;&quot;&gt;&quot;&#39;Today (&#39;MMMM d, yyyy&#39;)&#39;&quot;&lt;/span&gt;);
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawTextAsync(X, Y, Text, FONTINFO_ARIAL_11, TEXT_COLOR, TextAlign.Right,
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;TextBaseline.Middle);
  &lt;/div&gt;
  }
&lt;/div&gt;
  
  

&lt;br /&gt;&lt;br /&gt;
The next method to define is the &lt;i&gt;DrawStatusesAsync&lt;/i&gt; method.

&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;The DrawStatusesAsync method&lt;/div&gt;

&lt;br /&gt;
The &lt;i&gt;DrawStatusesAsync&lt;/i&gt; method first loops through the selected employee&#39;s time to see if there&#39;s time at the current date on the timeline and if the time&#39;s status is rejected, unsubmitted, under review, or approved. If a time entry was found for each status, the loop will exit.

&lt;br /&gt;&lt;br /&gt;
Following the loop, if time was found for a status, the &lt;i&gt;DrawTriangleAsync&lt;/i&gt; method is called to draw a triangle indicating the status.

&lt;br /&gt;&lt;br /&gt;
Add the code in the following snippet to the end of your &lt;i&gt;Index.razor&lt;/i&gt; file before the &lt;i&gt;@code&lt;/i&gt;&#39;s closing curly brace:
  
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;protected async&lt;/span&gt; Task DrawStatusesAsync(&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; X, &lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; Y, &lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; DayCellWidth,
  &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DateTime LoopDate)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;bool&lt;/span&gt; DayHasRejectedTime = &lt;span style=&quot;color:blue;&quot;&gt;false&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;bool&lt;/span&gt; DayHasUnsubmittedTime = &lt;span style=&quot;color:blue;&quot;&gt;false&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;bool&lt;/span&gt; DayHasUnderReviewTime = &lt;span style=&quot;color:blue;&quot;&gt;false&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;bool&lt;/span&gt; DayHasApprovedTime = &lt;span style=&quot;color:blue;&quot;&gt;false&lt;/span&gt;;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;string&lt;/span&gt; Status = &lt;span style=&quot;color:maroon;&quot;&gt;&quot;&quot;&lt;/span&gt;

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;foreach&lt;/span&gt; (TimeEntry Time &lt;span style=&quot;color:blue;&quot;&gt;in&lt;/span&gt; _TimeEntries)
    &lt;br /&gt;{
    &lt;div class=&quot;divIndent&quot;&gt;
      &lt;span style=&quot;color:darkgreen;&quot;&gt;// Exit the loop if it has passed the date needed&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (Time.TheDate &amp;gt; LoopDate) { &lt;span style=&quot;color:blue;&quot;&gt;break&lt;/span&gt;; }

      &lt;br /&gt;&lt;br /&gt;
      &lt;span style=&quot;color:darkgreen;&quot;&gt;// If the current item matches the date needed then...&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (Time.TheDate == LoopDate)
      &lt;br /&gt;{
      &lt;div class=&quot;divIndent&quot;&gt;
        Status = Time.Sheet.Status;
        &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (Status == &lt;span style=&quot;color:maroon;&quot;&gt;&quot;R&quot;&lt;/span&gt;) { DayHasRejectedTime = &lt;span style=&quot;color:blue;&quot;&gt;true&lt;/span&gt;; }
        &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;else if&lt;/span&gt; (Status == &lt;span style=&quot;color:maroon;&quot;&gt;&quot;N&quot;&lt;/span&gt;) { DayHasUnsubmittedTime = &lt;span style=&quot;color:blue;&quot;&gt;true&lt;/span&gt;; }
        &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;else if&lt;/span&gt; (Status == &lt;span style=&quot;color:maroon;&quot;&gt;&quot;U&quot;&lt;/span&gt;) { DayHasUnderReviewTime = &lt;span style=&quot;color:blue;&quot;&gt;true&lt;/span&gt;; }
        &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;else if&lt;/span&gt; (Status == &lt;span style=&quot;color:maroon;&quot;&gt;&quot;A&quot;&lt;/span&gt;) { DayHasApprovedTime = &lt;span style=&quot;color:blue;&quot;&gt;true&lt;/span&gt;; }

        &lt;br /&gt;&lt;br /&gt;
        &lt;span style=&quot;color:darkgreen;&quot;&gt;// If all four statuses have been found, exit the loop&lt;/span&gt;
        &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (DayHasRejectedTime &amp;amp;&amp;amp; DayHasUnsubmittedTime &amp;amp;&amp;amp; DayHasUnderReviewTime
        &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;&amp;amp; DayHasApprovedTime) { &lt;span style=&quot;color:blue;&quot;&gt;break&lt;/span&gt;; }
      &lt;/div&gt;
      }
    &lt;/div&gt;
    }

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt; Quarter = 0;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (DayHasUnsubmittedTime) 
    &lt;br /&gt;{
    &lt;div class=&quot;divIndent&quot;&gt;
      &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawTriangleAsync(X, (Y + (Quarter * DAY_QUARTER_HEIGHT)), DayCellWidth,
      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;STATUS_COLOR_UNSUBMITTED_FILL, STATUS_COLOR_UNSUBMITTED_BORDER);
    &lt;/div&gt;
    }

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (DayHasRejectedTime)
    &lt;br /&gt;{
    &lt;div class=&quot;divIndent&quot;&gt;
      Quarter = 1;
      &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawTriangleAsync(X, (Y + (Quarter * DAY_QUARTER_HEIGHT)), DayCellWidth,
      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;STATUS_COLOR_REJECTED_FILL, STATUS_COLOR_REJECTED_BORDER); 
    &lt;/div&gt;
    }

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (DayHasUnderReviewTime) 
    &lt;br /&gt;{
    &lt;div class=&quot;divIndent&quot;&gt;
      Quarter = 2;
      &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawTriangleAsync(X, (Y + (Quarter * DAY_QUARTER_HEIGHT)), DayCellWidth,      
      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;STATUS_COLOR_UNDER_REVIEW_FILL,
      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;STATUS_COLOR_UNDER_REVIEW_BORDER);
    &lt;/div&gt;
    }

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (DayHasApprovedTime) 
    &lt;br /&gt;{
    &lt;div class=&quot;divIndent&quot;&gt;
      Quarter = 3;
      &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawTriangleAsync(X, (Y + (Quarter * DAY_QUARTER_HEIGHT)), DayCellWidth,
      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;STATUS_COLOR_APPROVED_FILL, STATUS_COLOR_APPROVED_BORDER); 
    &lt;/div&gt;
    }
  &lt;/div&gt;
  }
&lt;/div&gt;
  
  

&lt;br /&gt;&lt;br /&gt;
The next method to define is the &lt;i&gt;DrawTriangleAsync&lt;/i&gt; method.

&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;The DrawTriangleAsync method&lt;/div&gt;

&lt;br /&gt;
The &lt;i&gt;DrawTriangleAsync&lt;/i&gt; method draws a triangle at the specified location using the colors provided.

&lt;br /&gt;&lt;br /&gt;
Add the code in the following snippet to the end of your &lt;i&gt;Index.razor&lt;/i&gt; file but before the &lt;i&gt;@code&lt;/i&gt;&#39;s closing curly brace:

&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;protected async&lt;/span&gt; Task DrawTriangleAsync(&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; X, &lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; Y, &lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; DayCellWidth,
  &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color:blue;&quot;&gt;string&lt;/span&gt; FillColor, &lt;span style=&quot;color:blue;&quot;&gt;string&lt;/span&gt; BorderColor)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// The triangle is 10 pixels wide and 14 pixels tall&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; XStart = (X + ((DayCellWidth - 10) / 2));
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; YStart = (Y + ((DAY_QUARTER_HEIGHT - 14) / 2));

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Set the line and fill colors&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.SetFillStyleAsync(FillColor);
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.SetStrokeStyleAsync(BorderColor);

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Plot the triangle&#39;s outline&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.BeginPathAsync();
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.MoveToAsync(XStart, YStart);
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.LineToAsync((XStart + 3), YStart);
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.LineToAsync((XStart + 9), (YStart + 6));
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.LineToAsync((XStart + 9), (YStart + 7));
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.LineToAsync((XStart + 3), (YStart + 13));
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.LineToAsync(XStart, (YStart + 13));
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.LineToAsync(XStart, YStart);

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Fill the triangle and then draw the outline&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.FillAsync(); 
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.StrokeAsync();
  &lt;/div&gt;
  }
&lt;/div&gt;
  
  

&lt;br /&gt;&lt;br /&gt;
The next method to define is the &lt;i&gt;DrawLegendAsync&lt;/i&gt; method.

&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;The DrawLegendAsync method&lt;/div&gt;

&lt;br /&gt;
The &lt;i&gt;DrawLegendAsync&lt;/i&gt; method draws a legend under the timeline, starting at the left edge of the canvas, to indicate which time entry status each triangle color represents.

&lt;br /&gt;&lt;br /&gt;
Add the code in the following snippet before the &lt;i&gt;@code&lt;/i&gt;&#39;s closing curly brace at the end of your &lt;i&gt;Index.razor&lt;/i&gt; file:
  
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;protected async&lt;/span&gt; Task DrawLegendAsync(&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; X, &lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; Y)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; TextY = (Y + _TextHeightArial11);

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Unsubmitted&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawTriangleAsync(X, Y, 10, STATUS_COLOR_UNSUBMITTED_FILL,
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;STATUS_COLOR_UNSUBMITTED_BORDER);
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; TextWidth = &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawTextAsync((X + 12), TextY, &lt;span style=&quot;color:maroon;&quot;&gt;&quot;Unsubmitted&quot;&lt;/span&gt;,
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FONTINFO_ARIAL_11, Baseline: TextBaseline.Middle);

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Rejected&lt;/span&gt;
    &lt;br /&gt;X += (12 + TextWidth + 8);
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawTriangleAsync(X, Y, 10, STATUS_COLOR_REJECTED_FILL,
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;STATUS_COLOR_REJECTED_BORDER);
    &lt;br /&gt;TextWidth = &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawTextAsync((X + 12), TextY, &lt;span style=&quot;color:maroon;&quot;&gt;&quot;Rejected&quot;&lt;/span&gt;, FONTINFO_ARIAL_11,
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Baseline: TextBaseline.Middle);

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Under Review&lt;/span&gt;
    &lt;br /&gt;X += (12 + TextWidth + 8);
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawTriangleAsync(X, Y, 10, STATUS_COLOR_UNDER_REVIEW_FILL,
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;STATUS_COLOR_UNDER_REVIEW_BORDER);
    &lt;br /&gt;TextWidth = &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawTextAsync((X + 12), TextY, &lt;span style=&quot;color:maroon;&quot;&gt;&quot;Under Review&quot;&lt;/span&gt;, FONTINFO_ARIAL_11,
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Baseline: TextBaseline.Middle);

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Approved&lt;/span&gt;
    &lt;br /&gt;X += (12 + TextWidth + 8);
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawTriangleAsync(X, Y, 10, STATUS_COLOR_APPROVED_FILL,
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;STATUS_COLOR_APPROVED_BORDER);
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawTextAsync((X + 12), TextY, &lt;span style=&quot;color:maroon;&quot;&gt;&quot;Approved&quot;&lt;/span&gt;, FONTINFO_ARIAL_11,
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Baseline: TextBaseline.Middle);
  &lt;/div&gt;
  }
&lt;/div&gt;
  
  

&lt;br /&gt;&lt;br /&gt;
The next method you need to define is the &lt;i&gt;DrawProcessingAsync&lt;/i&gt; method.

&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;The DrawProcessingAsync method&lt;/div&gt;

&lt;br /&gt;
If the view indicates that there&#39;s processing occurring, the &lt;i&gt;DrawProcessingAsync&lt;/i&gt; method draws an opaque layer over the canvas and then draws the text &lt;i&gt;&quot;Processing... One moment please.&quot;&lt;/i&gt; horizontally and vertically centered on the canvas.

&lt;br /&gt;&lt;br /&gt;
Add the code in the following snippet before the &lt;i&gt;@code&lt;/i&gt;&#39;s closing curly brace at the end of your &lt;i&gt;Index.razor&lt;/i&gt; file:
  
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;protected async&lt;/span&gt; Task DrawProcessingAsync(&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; Width, &lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; Height)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// If the view is processing then...&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (_IndicateProcessing)
    &lt;br /&gt;{
    &lt;div class=&quot;divIndent&quot;&gt;
      &lt;span style=&quot;color:darkgreen;&quot;&gt;// Draw an opaque background over the timeline&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.SetFillStyleAsync(PROCESSING_TEXT_BACKGROUND_COLOR);
      &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.FillRectAsync(0, 0, Width, Height);

      &lt;br /&gt;&lt;br /&gt;
      &lt;span style=&quot;color:darkgreen;&quot;&gt;// Text is horizontally and vertically centered in the space given&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; DrawTextAsync((Width / 2), ((Height - _TextHeightArial26Bold) / 2),
      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color:maroon;&quot;&gt;&quot;Processing... One moment please.&quot;&lt;/span&gt;, FONTINFO_ARIAL_26_BOLD,
      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PROCESSING_TEXT_COLOR, TextAlign.Center, TextBaseline.Top);
    &lt;/div&gt;
    }
  &lt;/div&gt;
  }
&lt;/div&gt;
  
  

&lt;br /&gt;&lt;br /&gt;
The final method to define is the &lt;i&gt;DrawTextAsync&lt;/i&gt; method.

&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;The DrawTextAsync method&lt;/div&gt;

&lt;br /&gt;
The &lt;i&gt;DrawTextAsync&lt;/i&gt; method draws the text at the location specified and accepts several optional parameters for controlling the text color and alignment. The method also returns the width of the text that was drawn in the event the calling method needs that information.

&lt;br /&gt;&lt;br /&gt;
Add the code in the following snippet before the &lt;i&gt;@code&lt;/i&gt;&#39;s closing curly brace at the end of your &lt;i&gt;Index.razor&lt;/i&gt; file:
  
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;protected async&lt;/span&gt; Task&amp;lt;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt;&amp;gt; DrawTextAsync(&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; X, &lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; Y, &lt;span style=&quot;color:blue;&quot;&gt;string&lt;/span&gt; Text,
  &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color:blue;&quot;&gt;string&lt;/span&gt; FontInfo = FONTINFO_ARIAL_9, &lt;span style=&quot;color:blue;&quot;&gt;string&lt;/span&gt; TextColor = TEXT_COLOR,
  &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;TextAlign Align = TextAlign.Left, TextBaseline Baseline = TextBaseline.Alphabetic)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.SetFontAsync(FontInfo);
    &lt;br /&gt;TextMetrics Metrics = &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.MeasureTextAsync(Text);

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.SetFillStyleAsync(TextColor);
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.SetTextAlignAsync(Align);
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.SetTextBaselineAsync(Baseline);
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;await&lt;/span&gt; _context.FillTextAsync(Text, X, Y);

    &lt;br /&gt;&lt;br /&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;return&lt;/span&gt; Metrics.Width;
  &lt;/div&gt;
  }
&lt;/div&gt;
  
  

&lt;br /&gt;&lt;br /&gt;
That&#39;s it for the Time Entry Status view itself. 

&lt;br /&gt;&lt;br /&gt;
The next section is optional and will walk you through adjusting a few things with the Blazor application to customize it. If you wish to skip the customizations, you can jump ahead to the &lt;b&gt;Viewing the results&lt;/b&gt; section.

&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;Customizing the application&lt;/div&gt;

&lt;br /&gt;
When you create a Blazor WebAssembly application, the template adds three views to show you some of what&#39;s possible. You just reworked the Home (&lt;i&gt;Index&lt;/i&gt;) view but the other two views, &lt;i&gt;Counter&lt;/i&gt; and &lt;i&gt;Fetch data&lt;/i&gt;, aren&#39;t needed. 

&lt;br /&gt;&lt;br /&gt;
The following are the steps to remove the &lt;i&gt;Counter&lt;/i&gt; and &lt;i&gt;Fetch data&lt;/i&gt; views:
&lt;ul style=&quot;list-style-type:disc;&quot;&gt;
  &lt;li&gt;In the &lt;i&gt;Solution Explorer&lt;/i&gt;
    &lt;ul style=&quot;list-style-type:circle;&quot;&gt;
      &lt;li&gt;Expand the &lt;i&gt;Pages&lt;/i&gt; folder and delete the following files:
        &lt;ul style=&quot;list-style-type:square;&quot;&gt;
          &lt;li&gt;&lt;i&gt;Counter.razor&lt;/i&gt;&lt;/li&gt;
          &lt;li&gt;&lt;i&gt;FetchData.razor&lt;/i&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;

      &lt;li&gt;Expand the &lt;i&gt;wwwroot&lt;/i&gt; folder
        &lt;ul style=&quot;list-style-type:square;&quot;&gt;
          &lt;li&gt;Delete the &lt;i&gt;weather.json&lt;/i&gt; file that&#39;s in the &lt;i&gt;sample-data&lt;/i&gt; folder&lt;/li&gt;
          &lt;li&gt;Delete the &lt;i&gt;sample-data&lt;/i&gt; folder&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      
      &lt;li&gt;Expand the &lt;i&gt;Shared&lt;/i&gt; folder and open the &lt;i&gt;NavMenu.razor&lt;/i&gt; file
        &lt;ul style=&quot;list-style-type:square;&quot;&gt;
          &lt;li&gt;At the top of the page, there&#39;s an &lt;i&gt;a&lt;/i&gt; tag. Change the text from &lt;i&gt;BlazorTimeEntryStatus&lt;/i&gt; to &lt;i&gt;Blazor - Time Entry Status&lt;/i&gt;&lt;/li&gt;

          &lt;li&gt;Delete the &lt;i&gt;li&lt;/i&gt; tags for the &lt;i&gt;Counter&lt;/i&gt; and &lt;i&gt;Fetch&lt;/i&gt; data items shown in the following snippet:
            
            &lt;br /&gt;&lt;br /&gt;
            &lt;div class=&quot;divExample&quot;&gt;
              &amp;lt;li class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;nav-item px-3&quot;&lt;/span&gt;&amp;gt;
              &lt;div class=&quot;divIndent&quot;&gt;
                &amp;lt;navlink class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;nav-link&quot;&lt;/span&gt; href=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;counter&quot;&lt;/span&gt;&amp;gt;
                &lt;div class=&quot;divIndent&quot;&gt;
                  &amp;lt;span aria-hidden=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;true&quot;&lt;/span&gt; class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;oi oi-plus&quot;&lt;/span&gt;&amp;gt;&amp;lt;/span&amp;gt; Counter
                &lt;/div&gt;
                &amp;lt;/navlink&amp;gt;
              &lt;/div&gt;
              &amp;lt;/li&amp;gt;
              &lt;br /&gt;&amp;lt;li class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;nav-item px-3&quot;&lt;/span&gt;&amp;gt;
              &lt;div class=&quot;divIndent&quot;&gt;
                &amp;lt;navlink class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;nav-link&quot;&lt;/span&gt; href=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;fetchdata&quot;&lt;/span&gt;&amp;gt;
                &lt;div class=&quot;divIndent&quot;&gt;
                  &amp;lt;span aria-hidden=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;true&quot;&lt;/span&gt; class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;oi oi-list-rich&quot;&lt;/span&gt;&amp;gt;&amp;lt;/span&amp;gt; Fetch data
                &lt;/div&gt;
                &amp;lt;/navlink&amp;gt;
              &lt;/div&gt;
              &amp;lt;/li&amp;gt;
            &lt;/div&gt;
          &lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;
  
  
&lt;br /&gt;
To remove the &lt;i&gt;About bar&lt;/i&gt; at the top of the Time Entry Status view:
  
&lt;ul style=&quot;list-style-type:disc;&quot;&gt;
  &lt;li&gt;In the &lt;i&gt;Solution Explorer&lt;/i&gt;, expand the &lt;i&gt;Shared&lt;/i&gt; folder
    &lt;ul style=&quot;list-style-type:circle;&quot;&gt;
      &lt;li&gt;Delete the &lt;i&gt;SurveyPrompt.razor&lt;/i&gt; file&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;

  &lt;li&gt;Open the &lt;i&gt;MainLayout.razor&lt;/i&gt; file and delete the following lines of markup that are within the &lt;i&gt;&amp;lt;div class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;main&quot;&lt;/span&gt;&amp;gt;&lt;/i&gt; div:
  
    &lt;br /&gt;&lt;br /&gt;
    &lt;div class=&quot;divExample&quot;&gt;
      &amp;lt;div class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;top-row px-4&quot;&lt;/span&gt;&amp;gt;
      &lt;div class=&quot;divIndent&quot;&gt;
        &amp;lt;a class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;ml-md-auto&quot;&lt;/span&gt; href=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;http://blazor.net&quot;&lt;/span&gt; target=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;_blank&quot;&lt;/span&gt;&amp;gt;About&amp;lt;/a&amp;gt;
      &lt;/div&gt;
      &amp;lt;/div&amp;gt;
    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;


  
&lt;br /&gt;&lt;br /&gt;
With the customizations to the Blazor WebAssembly application complete, it&#39;s time to view the results.

&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;Viewing the results&lt;/div&gt;

&lt;br /&gt;
As shown in the following image, if you run the application, you&#39;ll see the Time Entry Status view with your C# code running in a browser via WebAssembly!

&lt;br /&gt;&lt;br /&gt;
Clicking on an employee in the list should show you the statuses of any time that has been entered for that employee in the previous 30 days. Note that the list of employees and statuses will differ if you&#39;re connected to a different Dovico Timesheet database.
  
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear:both;text-align:center;&quot;&gt;
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUY9ypHqrtEo3TBiQv17oGFpuuxOpW4OH0-jlHKTZd2-ttW9gjKPgCQQ4BypCt6K2ScLwxrHbv3882o85oZz-p9RzUiUL7f9NxDlwICzpDHeqV0FNPEhuKRIZUU3mL0P46Vck_Npqqul0/s0/1+-+ScreenShotOfTheBlazorApp.png&quot; style=&quot;margin-left:1em;margin-right:1em;&quot;&gt;
    &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;579&quot; data-original-width=&quot;908&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUY9ypHqrtEo3TBiQv17oGFpuuxOpW4OH0-jlHKTZd2-ttW9gjKPgCQQ4BypCt6K2ScLwxrHbv3882o85oZz-p9RzUiUL7f9NxDlwICzpDHeqV0FNPEhuKRIZUU3mL0P46Vck_Npqqul0/s0/1+-+ScreenShotOfTheBlazorApp.png&quot; width=&quot;580&quot;/&gt;
  &lt;/a&gt;
    &lt;br /&gt;(click to view the image full size)
&lt;/div&gt;
  
  

&lt;br /&gt;&lt;br /&gt;
Before I end this article, I want to point out some security considerations that apply not only to Blazor WebAssembly applications but also web applications in general.

&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;Security considerations&lt;/div&gt;

&lt;br /&gt;
Because you&#39;re writing code with C#, it&#39;s easy to forget that the code isn&#39;t running on a server when you build a Blazor WebAssembly application. All of your code is downloaded and executed in the browser by the mono runtime. You shouldn&#39;t include anything in your codebase that you don&#39;t want others to see.

&lt;br /&gt;&lt;br /&gt;
Also, even though you may use code like the &lt;i&gt;HttpClient&lt;/i&gt; class, under the hood, Blazor is actually using the browser&#39;s &lt;i&gt;Fetch API&lt;/i&gt; to make the HTTP calls. It&#39;s really easy to inspect HTTP requests from a web page. For example, the following screenshot is showing the network tab of the Chrome browser&#39;s developer tools when the employee, Nancy Johnson, is selected. As you can see in the bottom-right corner, the request headers are visible:

&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear:both;text-align:center;&quot;&gt;
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyvOA0WAGQcL1RjHqWIHmv8Cu552krgkgF9aPr49FDolEKCidSua76iweBVxP-sCHDrGIBzC1fs42oZg0IR4Qxlyz8np6HPV0uLNAUfS2MU1MeD2jG5UrF91HN0V-WRsK1xdQIYai8d3U/s0/10+-+network+tab+of+chrome.png&quot; style=&quot;margin-left:1em;margin-right:1em;&quot;&gt;
    &lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;796&quot; data-original-width=&quot;884&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyvOA0WAGQcL1RjHqWIHmv8Cu552krgkgF9aPr49FDolEKCidSua76iweBVxP-sCHDrGIBzC1fs42oZg0IR4Qxlyz8np6HPV0uLNAUfS2MU1MeD2jG5UrF91HN0V-WRsK1xdQIYai8d3U/s0/10+-+network+tab+of+chrome.png&quot; width=&quot;580&quot;/&gt;
  &lt;/a&gt;
&lt;/div&gt;


  
&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;Summary&lt;/div&gt;

&lt;br /&gt;
In this article you learned the following:
&lt;ul&gt;
  &lt;li&gt;A Blazor WebAssembly application allows C# code to run in the browser via the mono runtime which is compiled to WebAssembly.&lt;/li&gt;
  &lt;li&gt;Blazor uses razor pages which are a combination of razor markup, HTML and C# code. All code in the razor file is included within an @code{} block.&lt;/li&gt;
  &lt;li&gt;The @inject directive is used to inject a service into your page.&lt;/li&gt;
  &lt;li&gt;The @ref attribute is used to give your C# code a reference to an HTML element.&lt;/li&gt;
  &lt;li&gt;The HttpClient object&#39;s DefaultRequestHeaders object can be given header values that all subsequent HTTP requests will use.&lt;/li&gt;
  &lt;li&gt;The Blazor.Extensions.Canvas nuget package allows your C# code to interact with an HTML Canvas element more naturally.&lt;/li&gt;
  &lt;li&gt;Blazor has a number of life cycle methods including OnInitializedAsync and OnAfterRenderAsync. A canvas element is not available until the OnAfterRenderAsync method.&lt;/li&gt;
  &lt;li&gt;You shouldn&#39;t include anything in your codebase that you don&#39;t want anyone outside your organization seeing. You also need to be careful not to include sensitive information in your HTTP requests because they can be easily inspected in the browser.&lt;/li&gt;
&lt;/ul&gt;

  

&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;font-weight: bold;&quot;&gt;Source Code&lt;/span&gt;
  &lt;br /&gt;&lt;br /&gt;
  The source code for this article can be found in the following github repository: &lt;a href=&quot;https://github.com/cggallant/blog_post_code/tree/master/2020-October-BlazorTimeEntryStatus&quot; target=&quot;_blank&quot;&gt;https://github.com/cggallant/blog_post_code/tree/master/2020-October-BlazorTimeEntryStatus&lt;/a&gt;
&lt;/div&gt;


&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot; style=&quot;text-align:justify;&quot;&gt;
  &lt;span style=&quot;font-weight: bold;&quot;&gt;Additional Material on WebAssembly&lt;/span&gt;
  &lt;br /&gt;&lt;br /&gt;
    
  Like what you read and are interested in learning more about WebAssembly? 
  &lt;ul&gt;
    &lt;li&gt;Check out my book &lt;a href=&quot;https://www.manning.com/books/webassembly-in-action&quot; target=&quot;_blank&quot;&gt;&quot;WebAssembly in Action&quot;&lt;/a&gt;
      &lt;br /&gt;&lt;br /&gt;
      &lt;div class=&quot;divIndent&quot;&gt;

        &lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjihyphenhyphenZY09x-tS5RRfD_ztLXykEYG_3DF_dcqI-OgeUnV2EwVHQOejfYW0xAX_UVzCrJeAu7nO5YCiwg0ti7U3vPrZT9NWbfOYn1yHrHw01I271gLKRYe57ajnG_x07VsubSplJiQdhH3og/s0/New+Book+Cover_200px+tall.jpg&quot; align=&quot;right&quot; style=&quot;margin-left:8px;&quot; height=&quot;190&quot; /&gt;
        
        The book introduces the WebAssembly stack and walks you through the process of writing and running browser-based applications. It also covers dynamic linking multiple modules at runtime, using web workers to prefetch a module, threading, using WebAssembly modules in Node.js, working with the WebAssembly text format, debugging, and more.
      
        &lt;br /&gt;&lt;br /&gt;
        The first chapter is free to read and, if you&#39;d like to buy the book, it&#39;s 40% off with the following code: &lt;b&gt;ggallantbl&lt;/b&gt;
      &lt;/div&gt;
      
      &lt;br /&gt;
    &lt;/li&gt;

    &lt;li&gt;&lt;a href=&quot;https://platform.uno/blog/using-webassembly-modules-in-c/ &quot; target=&quot;_blank&quot;&gt;Using WebAssembly modules in C#&lt;/a&gt;
      &lt;br /&gt;&lt;br /&gt;
      &lt;div class=&quot;divIndent&quot;&gt;
        
        &lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEii4ClFmF5k5JSjvNoomdgQli9O5Od-A-6WFsMRV9CLuDYAicxqapFf_UDUy1aCBttiLq4vEvT546yigg3-GONhCJNVcevTjwNXH1RgK1iP3zfO5TnNgj8D5Tcwrl87kprd__Bqf_OohVY/s711/z_+WebAssembly+in+C%2523_+image+for+my+blog+post+and+social+media.png&quot; width=&quot;300&quot; align=&quot;right&quot; style=&quot;margin-left:8px;&quot; /&gt;
        
        While there were a lot of exciting things being worked on with the WebAssembly System Interface (WASI) at the time of my book&#39;s writing, unfortunately, it wasn&#39;t until after the book went to production that an early preview of the Wasmtime runtime was announced for .NET Core.

        &lt;br /&gt;&lt;br /&gt;
        I wrote this article to show you how your C# code can load and use a WebAssembly module via the Wasmtime runtime for .NET. The article also covers how to create custom model validation with ASP.NET Core MVC.
      &lt;/div&gt;
      
      &lt;br /&gt;
    &lt;/li&gt;

    &lt;li&gt;&lt;a href=&quot;https://cggallant.blogspot.com/2020/07/webassembly-threads-in-firefox.html&quot; target=&quot;_blank&quot;&gt;WebAssembly threads in Firefox&lt;/a&gt;
      &lt;br /&gt;&lt;br /&gt;
      &lt;div class=&quot;divIndent&quot;&gt;
        
        &lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3Us0sM5ACYfCQInD0DVx-0uIMi6dYN8jDvV4g0F_zXfrJDo0WT8OJx7-2qnpMS_m-qeQ7VAFEKYTC4zWQaviYueoz3kXhIZVKeKnZqHUCY_IjAMdUEvIeQ_VC-C6kR46Am3Q8PvVfMMc/w400-h174/9+-+Screen+shot+of+final+product+with+images+shown.png&quot; width=&quot;300&quot; align=&quot;right&quot; style=&quot;margin-left:8px;&quot; /&gt; 
        
        My book shows you how to use WebAssembly threads but, at the time of its writing, they were only available in Firefox behind a flag. They&#39;re no longer behind a flag but Firefox has added a requirement: To enable the SharedArrayBuffer, you need to include two response headers.

        &lt;br /&gt;&lt;br /&gt;
        Although the headers are only required by Firefox desktop at the time of this article&#39;s writing, this will soon change as Chrome for Android will require the headers when version 88 is released in January 2021. Chrome desktop is expected to require the headers by March 2021.

        &lt;br /&gt;&lt;br /&gt;
        This article walks you through returning the response headers and using WebAssembly threads to convert a user-supplied image to greyscale.
      &lt;/div&gt;
      
      &lt;br /&gt;
    &lt;/li&gt;

    &lt;li&gt;&lt;a href=&quot;https://cggallant.blogspot.com/2020/01/the-import-statement-with-emscripten.html&quot; target=&quot;_blank&quot;&gt;Using the import statement with an Emscripten-generated WebAssembly module in Vue.js&lt;/a&gt;
      &lt;br /&gt;&lt;br /&gt;
      &lt;div class=&quot;divIndent&quot;&gt;
        
        &lt;img src=&quot;https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Vue.js_Logo_2.svg/200px-Vue.js_Logo_2.svg.png&quot; width=&quot;50&quot; align=&quot;right&quot; style=&quot;margin-left:8px;&quot; /&gt;
        
        Over the 2019 Christmas break, I helped a developer find a way to import an Emscripten-generated WebAssembly module into Vue.js. This article details the solutions found.
      &lt;/div&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/div&gt;

  
  
&lt;br /&gt;&lt;br /&gt;
Disclaimer: I was not paid to write this article but but I do work for &lt;a href=&quot;https://www.dovico.com/&quot; target=&quot;_blank&quot;&gt;Dovico Software&lt;/a&gt;. I also receive royalties on the sale of the book &lt;a href=&quot;https://www.manning.com/books/webassembly-in-action&quot; target=&quot;_blank&quot;&gt;&quot;WebAssembly in Action&quot;&lt;/a&gt;.
    
 
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/8642462671454240176/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2020/10/blazor-webassembly-and-dovico-time.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/8642462671454240176'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/8642462671454240176'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2020/10/blazor-webassembly-and-dovico-time.html' title='Blazor WebAssembly and the Dovico Time Entry Status app'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJVqiv4rjZilNzxt5xAmCTvoCPFMqbhsQsP0y3i2ENF6ZcKoOBi3yAUKos4sqWbUoeuYydI80GdY0pJnvZ3cR5O7vpYHAJXZ8Uz2LiISpABKZ37n6tKqYtATBZkE1Mb9CRDLjoWGqjAzg/s72-c/_ShareImage.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-3090153443301014641</id><published>2020-08-29T00:00:00.000-03:00</published><updated>2020-08-29T07:33:23.240-03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Manning"/><category scheme="http://www.blogger.com/atom/ns#" term="Manning Publications"/><category scheme="http://www.blogger.com/atom/ns#" term="WebAssembly"/><category scheme="http://www.blogger.com/atom/ns#" term="WebAssembly in Action"/><title type='text'>WebAssembly in Action is Manning&#39;s Deal of the Day</title><content type='html'>&lt;div style=&quot;color: black; font-family: &amp;quot;arial&amp;quot;; text-align: justify;&quot;&gt;

  &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhR0UqcYTvW7UlBq03igURfGnJoxvJNh1W4a9iTsGmbRIR3R_wK6Nzoj3Qx2MICXEW7Eo3-0DY4rW9USFM_zVeL-eolksulwNvhcuPFsdeLgdz29rnUeoXzzwpo6un67KG_ZROngt9XNsM/s2048/full+book+with+50%2525+off+tag.png&quot; style=&quot;clear:left;float:left;margin-left:1em; margin-right:1em;&quot;&gt;
    &lt;img border=&quot;0&quot; data-original-height=&quot;2048&quot; data-original-width=&quot;1817&quot; height=&quot;328&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhR0UqcYTvW7UlBq03igURfGnJoxvJNh1W4a9iTsGmbRIR3R_wK6Nzoj3Qx2MICXEW7Eo3-0DY4rW9USFM_zVeL-eolksulwNvhcuPFsdeLgdz29rnUeoXzzwpo6un67KG_ZROngt9XNsM/w290-h328/full+book+with+50%2525+off+tag.png&quot; width=&quot;290&quot; /&gt;
    &lt;/a&gt;
  &lt;/div&gt;
    
  &quot;WebAssembly in Action&quot; is 50% off today &lt;i&gt;(August 29th, 2020)&lt;/i&gt; as part of Manning’s Deal of the Day. 

  &lt;br /&gt;&lt;br /&gt;
  Use code &lt;b&gt;dotd082920au&lt;/b&gt; at &lt;a href=&quot;https://bit.ly/2ECq0zt&quot; target=&quot;_blank&quot;&gt;https://bit.ly/2ECq0zt&lt;/a&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/3090153443301014641/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2020/08/webassembly-in-action-is-mannings-deal.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/3090153443301014641'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/3090153443301014641'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2020/08/webassembly-in-action-is-mannings-deal.html' title='WebAssembly in Action is Manning&#39;s Deal of the Day'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhR0UqcYTvW7UlBq03igURfGnJoxvJNh1W4a9iTsGmbRIR3R_wK6Nzoj3Qx2MICXEW7Eo3-0DY4rW9USFM_zVeL-eolksulwNvhcuPFsdeLgdz29rnUeoXzzwpo6un67KG_ZROngt9XNsM/s72-w290-h328-c/full+book+with+50%2525+off+tag.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-8787017322051673640</id><published>2020-08-24T13:00:00.007-03:00</published><updated>2020-10-10T16:36:03.086-03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term=".NET Core"/><category scheme="http://www.blogger.com/atom/ns#" term="ASP.NET"/><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><category scheme="http://www.blogger.com/atom/ns#" term="C++"/><category scheme="http://www.blogger.com/atom/ns#" term="Emscripten"/><category scheme="http://www.blogger.com/atom/ns#" term="Wasmtime"/><category scheme="http://www.blogger.com/atom/ns#" term="WebAssembly"/><title type='text'>Using WebAssembly modules in C#</title><content type='html'>&lt;span style=&quot;display:none;&quot;&gt;An article showing you how your C# code can load and use a WebAssembly module via the Wasmtime runtime for .NET. It also covers how to create custom model validation with ASP.NET Core MVC.

&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;627&quot; data-original-width=&quot;1200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsR6_XG8ByrLygMsn0vy6D52j8oXpMQT849I1bMfeCURviEFflP3_XYVVVzBBmfrq3j4NziJ_iqaTsoRzA_hT9eGhuTuWxk47-63ywJvqmNhuVCYXQuCUYUaA4M_dXKPrbvxWYi49ZSOY/s0/_ShareImage.png&quot;/&gt;
&lt;/span&gt;


&lt;div style=&quot;color:black;font-family:arial;text-align:justify;&quot;&gt;
  
  In my book &lt;a href=&quot;https://www.manning.com/books/webassembly-in-action&quot; target=&quot;_blank&quot;&gt;&quot;WebAssembly in Action&quot;&lt;/a&gt;, I showed you how to use an Emscripten-generated WebAssembly module in the browser and on the server in Node.js.

  &lt;br /&gt;&lt;br /&gt;
  I briefly talked about the &lt;a href=&quot;https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/&quot; target=&quot;_blank&quot;&gt;WebAssembly System Interface (WASI)&lt;/a&gt; whose aim is to create a standard approach to running WebAssembly modules outside the browser in a safe way. 

  &lt;br /&gt;&lt;br /&gt;
  There were a lot of exciting things being worked on with WASI at the time but, unfortunately, it wasn&#39;t until after the book went to production that an early preview of the Wasmtime runtime was announced for .NET Core.

  &lt;br /&gt;&lt;br /&gt;
  Today I&#39;m pleased to announce that I wrote an article to show you how your C# code can load and use a WebAssembly module via the Wasmtime runtime for .NET. It also covers how to create custom model validation with ASP.NET Core MVC. &lt;b&gt;&lt;a href=&quot;https://platform.uno/blog/using-webassembly-modules-in-c/&quot; target=&quot;_blank&quot;&gt;https://platform.uno/blog/using-webassembly-modules-in-c/&lt;/a&gt;&lt;/b&gt;
  
  &lt;br /&gt;&lt;br /&gt;  
  &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEii4ClFmF5k5JSjvNoomdgQli9O5Od-A-6WFsMRV9CLuDYAicxqapFf_UDUy1aCBttiLq4vEvT546yigg3-GONhCJNVcevTjwNXH1RgK1iP3zfO5TnNgj8D5Tcwrl87kprd__Bqf_OohVY/s711/z_+WebAssembly+in+C%2523_+image+for+my+blog+post+and+social+media.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
      &lt;img border=&quot;0&quot; data-original-height=&quot;440&quot; data-original-width=&quot;711&quot; height=&quot;317&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEii4ClFmF5k5JSjvNoomdgQli9O5Od-A-6WFsMRV9CLuDYAicxqapFf_UDUy1aCBttiLq4vEvT546yigg3-GONhCJNVcevTjwNXH1RgK1iP3zfO5TnNgj8D5Tcwrl87kprd__Bqf_OohVY/w512-h317/z_+WebAssembly+in+C%2523_+image+for+my+blog+post+and+social+media.png&quot; width=&quot;512&quot; /&gt;
    &lt;/a&gt;
  &lt;/div&gt;
  
  
  
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot; style=&quot;text-align:justify;&quot;&gt;
  &lt;span style=&quot;font-weight: bold;&quot;&gt;Additional Material on WebAssembly&lt;/span&gt;
  &lt;br /&gt;&lt;br /&gt;
    
  Like what you read and are interested in learning more about WebAssembly? 
  &lt;ul&gt;
    &lt;li&gt;Check out my book &lt;a href=&quot;https://www.manning.com/books/webassembly-in-action&quot; target=&quot;_blank&quot;&gt;&quot;WebAssembly in Action&quot;&lt;/a&gt;
      &lt;br /&gt;&lt;br /&gt;
      &lt;div class=&quot;divIndent&quot;&gt;

        &lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjihyphenhyphenZY09x-tS5RRfD_ztLXykEYG_3DF_dcqI-OgeUnV2EwVHQOejfYW0xAX_UVzCrJeAu7nO5YCiwg0ti7U3vPrZT9NWbfOYn1yHrHw01I271gLKRYe57ajnG_x07VsubSplJiQdhH3og/s0/New+Book+Cover_200px+tall.jpg&quot; align=&quot;right&quot; style=&quot;margin-left:8px;&quot; height=&quot;190&quot; /&gt;
        
        The book introduces the WebAssembly stack and walks you through the process of writing and running browser-based applications. It also covers dynamic linking multiple modules at runtime, using web workers to prefetch a module, threading, using WebAssembly modules in Node.js, working with the WebAssembly text format, debugging, and more.
      
        &lt;br /&gt;&lt;br /&gt;
        The first chapter is free to read and, if you&#39;d like to buy the book, it&#39;s 40% off with the following code: &lt;b&gt;ggallantbl&lt;/b&gt;
      &lt;/div&gt;
      
      &lt;br /&gt;
    &lt;/li&gt;

    
    &lt;li&gt;&lt;a href=&quot;https://cggallant.blogspot.com/2020/10/blazor-webassembly-and-dovico-time.html&quot; target=&quot;_blank&quot;&gt;Blazor WebAssembly and the Dovico Time Entry Status app&lt;/a&gt;
      &lt;br /&gt;&lt;br /&gt;
      &lt;div class=&quot;divIndent&quot;&gt;
        
        &lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgalLjxNn_Cvja4YjS5Hzwe1kElYuATuJzY-47Z__gNdhSPBjm-B4TKhLBA3QXWygKBm0u7W6LEh5CVPnn9CkSrtFkY_7bkJnLPRkDou2cdDsnIBs97hhecDcZDcXb7AiKzCPWE_Y53Mzw/w640-h408/1+-+ScreenShotOfTheBlazorApp.png&quot; width=&quot;300&quot; align=&quot;right&quot; style=&quot;margin-left:8px;&quot; /&gt;
        
        As I was digging into WebAssembly from a C# perspective for an article that I was preparing to write, I decided to use some research time that my company gave me to dig into Blazor WebAssembly by rewriting a small Java application that I built in 2011.

        &lt;br /&gt;&lt;br /&gt;
        This article walks you through creating the Dovico Time Entry Status app using Blazor WebAssembly.
      &lt;/div&gt;
  
      &lt;br /&gt;
    &lt;/li&gt;
    
    
    &lt;li&gt;&lt;a href=&quot;https://cggallant.blogspot.com/2020/07/webassembly-threads-in-firefox.html&quot; target=&quot;_blank&quot;&gt;WebAssembly threads in Firefox&lt;/a&gt;
      &lt;br /&gt;&lt;br /&gt;
      &lt;div class=&quot;divIndent&quot;&gt;
        
        &lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3Us0sM5ACYfCQInD0DVx-0uIMi6dYN8jDvV4g0F_zXfrJDo0WT8OJx7-2qnpMS_m-qeQ7VAFEKYTC4zWQaviYueoz3kXhIZVKeKnZqHUCY_IjAMdUEvIeQ_VC-C6kR46Am3Q8PvVfMMc/w400-h174/9+-+Screen+shot+of+final+product+with+images+shown.png&quot; width=&quot;300&quot; align=&quot;right&quot; style=&quot;margin-left:8px;&quot; /&gt; 
        
        My book shows you how to use WebAssembly threads but, at the time of its writing, they were only available in Firefox behind a flag. They&#39;re no longer behind a flag but Firefox has added a requirement: To enable the SharedArrayBuffer, you need to include two response headers.

        &lt;br /&gt;&lt;br /&gt;
        Although the headers are only required by Firefox desktop at the time of this article&#39;s writing, this will soon change as Chrome for Android will require the headers when version 88 is released in January 2021. Chrome desktop is expected to require the headers by March 2021.

        &lt;br /&gt;&lt;br /&gt;
        This article walks you through returning the response headers and using WebAssembly threads to convert a user-supplied image to greyscale.
      &lt;/div&gt;
      
      &lt;br /&gt;
    &lt;/li&gt;

    &lt;li&gt;&lt;a href=&quot;https://cggallant.blogspot.com/2020/01/the-import-statement-with-emscripten.html&quot; target=&quot;_blank&quot;&gt;Using the import statement with an Emscripten-generated WebAssembly module in Vue.js&lt;/a&gt;
      &lt;br /&gt;&lt;br /&gt;
      &lt;div class=&quot;divIndent&quot;&gt;
        
        &lt;img src=&quot;https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Vue.js_Logo_2.svg/200px-Vue.js_Logo_2.svg.png&quot; width=&quot;50&quot; align=&quot;right&quot; style=&quot;margin-left:8px;&quot; /&gt;
        
        Over the 2019 Christmas break, I helped a developer find a way to import an Emscripten-generated WebAssembly module into Vue.js. This article details the solutions found.
      &lt;/div&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/8787017322051673640/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2020/08/using-webassembly-modules-in-c.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/8787017322051673640'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/8787017322051673640'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2020/08/using-webassembly-modules-in-c.html' title='Using WebAssembly modules in C#'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsR6_XG8ByrLygMsn0vy6D52j8oXpMQT849I1bMfeCURviEFflP3_XYVVVzBBmfrq3j4NziJ_iqaTsoRzA_hT9eGhuTuWxk47-63ywJvqmNhuVCYXQuCUYUaA4M_dXKPrbvxWYi49ZSOY/s72-c/_ShareImage.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-2030959435400146484</id><published>2020-08-18T11:33:00.000-03:00</published><updated>2020-08-18T11:33:14.329-03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Emscripten"/><category scheme="http://www.blogger.com/atom/ns#" term="GitHub"/><category scheme="http://www.blogger.com/atom/ns#" term="WebAssembly"/><title type='text'>The &quot;WebAssembly in Action&quot; GitHub Repository</title><content type='html'>&lt;div style=&quot;color:black;font-family:arial;text-align:justify;&quot;&gt;
  
  &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNhTqmlzTzJB48utMjfgPdkQ9cDjcQH-kZWo9sqKmYWlIX3cPMRLIZoyH1uOgPT6rWEJEgNCoWOfgbOwt7MgbCw8r0PjT3z07N3Byi-pcEAGM53oYLCbKJ0U1cCANQIcZWer-nDBxsjJY/s2044/book+cover+with+github+in+corner_rounded.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
      &lt;img border=&quot;0&quot; data-original-height=&quot;1136&quot; data-original-width=&quot;2044&quot; height=&quot;178&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNhTqmlzTzJB48utMjfgPdkQ9cDjcQH-kZWo9sqKmYWlIX3cPMRLIZoyH1uOgPT6rWEJEgNCoWOfgbOwt7MgbCw8r0PjT3z07N3Byi-pcEAGM53oYLCbKJ0U1cCANQIcZWer-nDBxsjJY/w320-h178/book+cover+with+github+in+corner_rounded.png&quot; width=&quot;320&quot; /&gt;
    &lt;/a&gt;
  &lt;/div&gt;
  
  
  &lt;br /&gt;
  Last week, I created a GitHub repository for the original code from my book &lt;a href=&quot;https://www.manning.com/books/webassembly-in-action&quot; target=&quot;_blank&quot;&gt;&quot;WebAssembly in Action&quot;&lt;/a&gt;. 

  &lt;br /&gt;&lt;br /&gt;
  One of the tools used in the book is the Emscripten toolkit and, when the book went to print, the version used by the book was &lt;i&gt;1.38.45&lt;/i&gt;. Because the toolkit it constantly being improved, some of the items shown in the book need to be adjusted if you want to use a more recent version of Emscripten.

  &lt;br /&gt;&lt;br /&gt;
  I updated the &lt;a href=&quot;https://livebook.manning.com/book/webassembly-in-action/welcome/&quot; target=&quot;_blank&quot;&gt;liveBook&lt;/a&gt; version of my book with comments explaining the workarounds when things needed to be adjusted but it didn&#39;t feel like enough.

  &lt;br /&gt;&lt;br /&gt;
  Over the past week, whenever I could find a spare moment, I updated the original code to work with the latest version of Emscripten. Today I&#39;m pleased to announce that the GitHub repository now has an &lt;a href=&quot;https://github.com/cggallant/WebAssembly-in-Action/tree/master/updated-code&quot; target=&quot;_blank&quot;&gt;updated-code&lt;/a&gt; folder with the book&#39;s code adjusted to work with &lt;b&gt;Emscripten 2.0.0&lt;/b&gt;!

  &lt;br /&gt;&lt;br /&gt;
  If you&#39;re interested, the GitHub repository for &quot;WebAssembly in Action&quot; can be found here: &lt;a href=&quot;https://github.com/cggallant/WebAssembly-in-Action&quot; target=&quot;_blank&quot;&gt;https://github.com/cggallant/WebAssembly-in-Action&lt;/a&gt;

&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/2030959435400146484/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2020/08/the-webassembly-in-action-github.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/2030959435400146484'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/2030959435400146484'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2020/08/the-webassembly-in-action-github.html' title='The &quot;WebAssembly in Action&quot; GitHub Repository'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNhTqmlzTzJB48utMjfgPdkQ9cDjcQH-kZWo9sqKmYWlIX3cPMRLIZoyH1uOgPT6rWEJEgNCoWOfgbOwt7MgbCw8r0PjT3z07N3Byi-pcEAGM53oYLCbKJ0U1cCANQIcZWer-nDBxsjJY/s72-w320-h178-c/book+cover+with+github+in+corner_rounded.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-5808692067072333752</id><published>2020-07-28T16:40:00.014-03:00</published><updated>2020-10-10T16:42:02.247-03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="COEP"/><category scheme="http://www.blogger.com/atom/ns#" term="COOP"/><category scheme="http://www.blogger.com/atom/ns#" term="Cross-Origin-Embedder-Policy"/><category scheme="http://www.blogger.com/atom/ns#" term="Cross-Origin-Opener-Policy"/><category scheme="http://www.blogger.com/atom/ns#" term="crossorigin"/><category scheme="http://www.blogger.com/atom/ns#" term="Emscripten"/><category scheme="http://www.blogger.com/atom/ns#" term="Firefox"/><category scheme="http://www.blogger.com/atom/ns#" term="performance"/><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><category scheme="http://www.blogger.com/atom/ns#" term="require-corp"/><category scheme="http://www.blogger.com/atom/ns#" term="same-origin"/><category scheme="http://www.blogger.com/atom/ns#" term="SharedArrayBuffer"/><category scheme="http://www.blogger.com/atom/ns#" term="thread"/><category scheme="http://www.blogger.com/atom/ns#" term="wasm"/><category scheme="http://www.blogger.com/atom/ns#" term="Web Workers"/><category scheme="http://www.blogger.com/atom/ns#" term="WebAssembly"/><title type='text'>WebAssembly threads in Firefox</title><content type='html'>&lt;span style=&quot;display:none;&quot;&gt;This article walks you through returning the response headers needed to enable the SharedArrayBuffer in Firefox. It also shows you how to use WebAssembly threads to convert a user-supplied image to greyscale.

&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;627&quot; data-original-width=&quot;1200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1z1mAtb8zm9J830NMg18LyxnW1CjO1DXeppsYZJGnHfkErj7eGmAWdoMhimAunG7RDBgMEuXiAHOtODhZWUC1ecosBwfomqasAazLPvIcSEa-zPmIw4soLMG1HYUUUGByCO2xZ61pry8/s0/_ShareImage.png&quot;/&gt;
&lt;/span&gt;

&lt;div style=&quot;color: black; font-family:arial; text-align: justify;clear:none;&quot;&gt;
  
This article covers
&lt;ul&gt;
  &lt;li&gt;Returning the response headers needed to enable the SharedArrayBuffer in Firefox&lt;/li&gt;
  &lt;li&gt;Accessing and modifying the pixel information from an image file directly in your web page&lt;/li&gt;
  &lt;li&gt;Creating a WebAssembly module that uses pthreads (POSIX threads)&lt;/li&gt;
&lt;/ul&gt;
  
&lt;br /&gt;
WebAssembly modules leverage several browser features in order to support pthreads: The SharedArrayBuffer, web workers, and Atomics.

&lt;br /&gt;&lt;br /&gt;
The SharedArrayBuffer is similar to the ArrayBuffer that WebAssembly modules normally use but this buffer allows multiple threads to share the same block of memory. Each thread runs in its own web worker and Atomics are used to synchronize data between the threads in a safe way. 

&lt;br /&gt;&lt;br /&gt;
I won&#39;t cover Atomics in this article so, if you&#39;d like to learn more, you can visit the following web page: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics&quot; target=&quot;_blank&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics&lt;/a&gt; 

&lt;br /&gt;&lt;br /&gt;
In January 2018, the Spectre/Meltdown vulnerabilities forced browser makers to disable support for the SharedArrayBuffer. Since then, browser makers have been working on ways to prevent the exploit. By October 2018, Chrome was able to re-enable it for desktop versions of its browser by using site isolation.

&lt;br /&gt;&lt;br /&gt;
Firefox chose a different approach to prevent the exploit. Rather than site isolation, they only allow access to the SharedArrayBuffer if two response headers are provided. This new approach went live with Firefox 79 that was released on July 28th, 2020. 

&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divIndentBothSides&quot;&gt;
  NOTE: At the time of this article&#39;s writing, the response header approach isn&#39;t needed by Chrome, or Chromium-based browsers like Edge, because the desktop versions use site isolation. According to the following article, Chrome will require the response headers shown in this article in the near future too: &lt;a href=&quot;https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/_0MEXs6TJhg&quot; target=&quot;_blank&quot;&gt;https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/_0MEXs6TJhg&lt;/a&gt;
&lt;/div&gt;
  
&lt;br /&gt;
In this article you&#39;re going to learn how to enable the SharedArrayBuffer in Firefox so that you can use pthreads in a WebAssembly module. You&#39;ll learn how to load an image file from the device and access the pixel information so that you can adjust the image in the browser. Finally, you&#39;ll see how pthreads can be used to speed up the processing.

&lt;br /&gt;&lt;br /&gt;
Suppose you have a web service that lets your users upload an image to your server and download a modified version with various filters now applied. The web page works fine but is a little slow if the images are large because of all the data being uploaded and then downloaded once the modifications are complete. Using all that bandwidth also costs your customers money so you&#39;d like to move the processing from the server to the device.

&lt;br /&gt;&lt;br /&gt;
Rather than jumping in with both feet, you decide that it would be best to create a prototype in order to compare the speed of using JavaScript directly, using a WebAssembly module but without using pthreads, and then using a WebAssembly module with pthreads.

&lt;br /&gt;&lt;br /&gt;
To keep things simple for this test, the image will be converted to grayscale and then the web page will display each image along with how long it took to modify them as shown in the following image:

&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3Us0sM5ACYfCQInD0DVx-0uIMi6dYN8jDvV4g0F_zXfrJDo0WT8OJx7-2qnpMS_m-qeQ7VAFEKYTC4zWQaviYueoz3kXhIZVKeKnZqHUCY_IjAMdUEvIeQ_VC-C6kR46Am3Q8PvVfMMc/s1608/9+-+Screen+shot+of+final+product+with+images+shown.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
    &lt;img border=&quot;0&quot; data-original-height=&quot;696&quot; data-original-width=&quot;1608&quot; height=&quot;174&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3Us0sM5ACYfCQInD0DVx-0uIMi6dYN8jDvV4g0F_zXfrJDo0WT8OJx7-2qnpMS_m-qeQ7VAFEKYTC4zWQaviYueoz3kXhIZVKeKnZqHUCY_IjAMdUEvIeQ_VC-C6kR46Am3Q8PvVfMMc/w400-h174/9+-+Screen+shot+of+final+product+with+images+shown.png&quot; width=&quot;400&quot; /&gt;
  &lt;/a&gt;
  &lt;br /&gt;(click to view the image full size)
&lt;/div&gt;
  
&lt;br /&gt;
As shown below, the steps for building this web page are:
&lt;ol&gt;
  &lt;li&gt;Modify your web server to return the necessary response headers to enable the SharedArrayBuffer in Firefox&lt;/li&gt;
  &lt;li&gt;Create the web page and add the ability to load an image file from your user&#39;s device&lt;/li&gt;
  &lt;li&gt;Adjust the image using JavaScript for a comparison to the WebAssembly versions&lt;/li&gt;
  &lt;li&gt;Create a WebAssembly module that modifies the image without using threads and with threads to see the difference between the two&lt;/li&gt;
&lt;/ol&gt;
  
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNEUyxqNTkYiEgYJ3-1o_tNkIdh0LJ0FeqD6IZmvubhWjH0qieC2Qn_eMneWSpi7-TegAjo-c1O-d9oDZBsI8bTjdw-IhPhvY8HTfwbO4X1BwmXhRuM4-E8E-58AQTthA11pkNwQOrk2I/s2038/1+-+steps+needed+for+the+article.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
    &lt;img border=&quot;0&quot; data-original-height=&quot;1286&quot; data-original-width=&quot;2038&quot; height=&quot;253&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNEUyxqNTkYiEgYJ3-1o_tNkIdh0LJ0FeqD6IZmvubhWjH0qieC2Qn_eMneWSpi7-TegAjo-c1O-d9oDZBsI8bTjdw-IhPhvY8HTfwbO4X1BwmXhRuM4-E8E-58AQTthA11pkNwQOrk2I/w400-h253/1+-+steps+needed+for+the+article.png&quot; width=&quot;400&quot; /&gt;
  &lt;/a&gt;
  &lt;br /&gt;(click to view the image full size)
&lt;/div&gt;
  
&lt;br /&gt;
As the following image shows, your first step towards building this web page is to modify your web server.
  
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0zzNeM0VaeuI6quGEDFQllVQvL6JI3F_l5uMNrrNNYB3WIr0hVSaac6NU2hyphenhyphenbMLXpYzbLyC8lidIRV5ZeFC9XSC0kI5XiFJ1YbssCIrz8KYariukKeU8BOSzn206I40LERuqPaqMkYeQ/s805/2+-+the+1st+step.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;483&quot; data-original-width=&quot;805&quot; height=&quot;154&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0zzNeM0VaeuI6quGEDFQllVQvL6JI3F_l5uMNrrNNYB3WIr0hVSaac6NU2hyphenhyphenbMLXpYzbLyC8lidIRV5ZeFC9XSC0kI5XiFJ1YbssCIrz8KYariukKeU8BOSzn206I40LERuqPaqMkYeQ/w256-h154/2+-+the+1st+step.png&quot; width=&quot;256&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&quot;color: black; font-family: &amp;quot;arial&amp;quot;; text-align: justify;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;color: black; font-family: &amp;quot;arial&amp;quot;; text-align: justify;&quot;&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;1. Modify the web server&lt;/div&gt;
  
&lt;br /&gt;
In order to enable the SharedArrayBuffer in Firefox, you need to specify two response headers:
  
&lt;ul&gt;
  &lt;li&gt;
    &lt;b&gt;Cross-Origin-Opener-Policy&lt;/b&gt; (&lt;i&gt;COOP&lt;/i&gt;) with the value &lt;b&gt;same-origin&lt;/b&gt;. This prevents documents from other origins being loaded into the same browsing context. The following web page has more information on this header and the possible values: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy&quot; target=&quot;_blank&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy&lt;/a&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;b&gt;Cross-Origin-Embedder-Policy&lt;/b&gt; (&lt;i&gt;COEP&lt;/i&gt;) with the value &lt;b&gt;require-corp&lt;/b&gt;. This prevents the loading of any cross-origin resources that don&#39;t explicitly grant permission using COOP above or Cross-Origin Resource Sharing (&lt;i&gt;CORS&lt;/i&gt;). For more information on this header and the possible values, you can visit this web page: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy&quot; target=&quot;_blank&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy&lt;/a&gt;

    &lt;br /&gt;&lt;br /&gt;When you use the &lt;i&gt;require-corp&lt;/i&gt; value and try to load a document from a cross-origin location, like a CSS file from a CDN for example, that location will need to support CORS. If you trust that location, you also need to mark that file as loadable by including the &lt;i&gt;crossorigin&lt;/i&gt; attribute. You&#39;ll see the crossorigin attribute used later in this article.
  &lt;/li&gt;
&lt;/ul&gt;


&lt;br /&gt;
&lt;div class=&quot;divIndentBothSides&quot;&gt;
  &lt;div class=&quot;divExample&quot;&gt;
    &lt;b&gt;NOTE:&lt;/b&gt; If you&#39;re using &#39;localhost&#39; as your hostname (http://localhost:8080/ for example), Firefox will enable the SharedArrayBuffer if you specify the COOP and COEP response headers. If you use any other hostname, Firefox will only enable the SharedArrayBuffer if you use HTTPS with a valid certificate.
  &lt;/div&gt;
&lt;/div&gt;
  
  
&lt;br /&gt;
For this article, I&#39;m going to use Python as the web server but you can use any web server you&#39;re comfortable with.

&lt;br /&gt;&lt;br /&gt;
Create a &lt;i&gt;frontend&lt;/i&gt; folder for the web page files that you&#39;ll create in this article.

&lt;br /&gt;&lt;br /&gt;
If you choose to use your own web server, feel free to skip to the end of this section and continue on with section &quot;&lt;b&gt;2. Create the web page&lt;/b&gt;&quot; once you&#39;ve adjusted your web server to return the response headers with the required values.

&lt;br /&gt;&lt;br /&gt;
You will need to modify the &lt;i&gt;wasm-server.py&lt;/i&gt; file that was created in the &quot;&lt;a href=&quot;https://cggallant.blogspot.com/2020/07/extending-pythons-simple-http-server.html&quot; target=&quot;_blank&quot;&gt;Extending Python&#39;s Simple HTTP Server&lt;/a&gt;&quot; article. If you didn&#39;t follow along with that article, the files can be found here:
  
&lt;ul&gt;
  &lt;li&gt;For &lt;i&gt;Python 2.x&lt;/i&gt;: &lt;a href=&quot;https://github.com/cggallant/blog_post_code/blob/master/2020%20-%20July%20-%20Extending%20Python%E2%80%99s%20Simple%20HTTP%20Server/Python%202.x/wasm-server.py&quot; target=&quot;_blank&quot;&gt;wasm-server.py&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;For &lt;i&gt;Python 3.x&lt;/i&gt;: &lt;a href=&quot;https://github.com/cggallant/blog_post_code/blob/master/2020%20-%20July%20-%20Extending%20Python%E2%80%99s%20Simple%20HTTP%20Server/Python%203.x/wasm-server.py&quot; target=&quot;_blank&quot;&gt;wasm-server.py&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
  
&lt;br /&gt;
Place the &lt;i&gt;wasm-server.py&lt;/i&gt; file in the &lt;i&gt;frontend&lt;/i&gt; folder and then open it with your favorite editor.

&lt;br /&gt;&lt;br /&gt;
In the &lt;i&gt;end_headers&lt;/i&gt; method, there&#39;s a comment showing the syntax necessary if you wanted to include a CORS header. This is where you&#39;ll add the COOP and COEP headers.

&lt;br /&gt;&lt;br /&gt;
Delete the two comments above the &lt;i&gt;SimpleHTTPServer.SimpleHTTPRequestHandler.end_headers(self)&lt;/i&gt; line of code and replace them with the following:
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;self&lt;/span&gt;.send_header(&lt;span style=&quot;color: maroon;&quot;&gt;&quot;Cross-Origin-Opener-Policy&quot;&lt;/span&gt;, &lt;span style=&quot;color: maroon;&quot;&gt;&quot;same-origin&quot;&lt;/span&gt;)
  &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;self&lt;/span&gt;.send_header(&lt;span style=&quot;color: maroon;&quot;&gt;&quot;Cross-Origin-Embedder-Policy&quot;&lt;/span&gt;, &lt;span style=&quot;color: maroon;&quot;&gt;&quot;require-corp&quot;&lt;/span&gt;)
&lt;/div&gt;
 
&lt;br /&gt;
Your class should now look like the following code snippet:
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;class&lt;/span&gt; WasmHandler(SimpleHTTPRequestHandler):
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color: blue;&quot;&gt;def&lt;/span&gt; end_headers(&lt;span style=&quot;color: blue;&quot;&gt;self&lt;/span&gt;):
    &lt;div class=&quot;divIndent&quot;&gt;
      &lt;span style=&quot;color: blue;&quot;&gt;self&lt;/span&gt;.send_header(&lt;span style=&quot;color: maroon;&quot;&gt;&quot;Cross-Origin-Opener-Policy&quot;&lt;/span&gt;, &lt;span style=&quot;color: maroon;&quot;&gt;&quot;same-origin&quot;&lt;/span&gt;)
      &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;self&lt;/span&gt;.send_header(&lt;span style=&quot;color: maroon;&quot;&gt;&quot;Cross-Origin-Embedder-Policy&quot;&lt;/span&gt;, &lt;span style=&quot;color: maroon;&quot;&gt;&quot;require-corp&quot;&lt;/span&gt;)
      &lt;br /&gt;SimpleHTTPRequestHandler.end_headers(&lt;span style=&quot;color: blue;&quot;&gt;self&lt;/span&gt;)
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
  
&lt;br /&gt;
Save the &lt;i&gt;wasm-server.py&lt;/i&gt; file.

&lt;br /&gt;&lt;br /&gt;
As shown in the following image, your next step is to build the HTML file that will allow a user to open an image file. The HTML file will have four canvas tags with one to show the original image and three to show the grayscale images along with how long the different approaches take to complete.

&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgE71gbIfP9FyMwAX4hj8egMiPjvT5nNSAQ7aDO580NGsyFxPVX_WykoQ5Iz9xTqSsZAVuhi1xovXSOsY8eehI2NS5TNKUADy39qjhubidDML2lSjR74Z5DO9iqL80LQ7Krd05hHM1TpWg/s2038/3+-+the+2nd+step.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
    &lt;img border=&quot;0&quot; data-original-height=&quot;739&quot; data-original-width=&quot;2038&quot; height=&quot;145&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgE71gbIfP9FyMwAX4hj8egMiPjvT5nNSAQ7aDO580NGsyFxPVX_WykoQ5Iz9xTqSsZAVuhi1xovXSOsY8eehI2NS5TNKUADy39qjhubidDML2lSjR74Z5DO9iqL80LQ7Krd05hHM1TpWg/w400-h145/3+-+the+2nd+step.png&quot; width=&quot;400&quot; /&gt;
  &lt;/a&gt;
  &lt;br /&gt;(click to view the image full size)
&lt;/div&gt;

  
  
&lt;br /&gt;  
&lt;div style=&quot;font-weight: bold;&quot;&gt;2. Create the web page&lt;/div&gt;
 
&lt;br /&gt;
Most of the HTML for the web page is boilerplate code so I&#39;ll only point out key items and will present the full file at the end of this section.

&lt;br /&gt;&lt;br /&gt;
You&#39;ll be using the Bootstrap web development framework because it offers a professional-looking web page which is faster than styling everything manually. The files needed for Bootstrap will loaded from a CDN rather than having to download the libraries.

&lt;br /&gt;&lt;br /&gt;
Because you&#39;ll be linking to files from a CDN, they&#39;re not coming from the same origin as your web page and will be blocked by default because you specified the &lt;i&gt;require-corp&lt;/i&gt; value for the &lt;i&gt;COEP&lt;/i&gt; header. You can include the &lt;i&gt;crossorigin&lt;/i&gt; attribute in the links for the CDN files in order to allow them to be downloaded. As an example, the following JavaScript link specifies the crossorigin attribute because it&#39;s hosted on a Google server: 
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &amp;lt;script src=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js&quot;&lt;/span&gt; &lt;b&gt;crossorigin&lt;/b&gt;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/div&gt;
  
&lt;br /&gt;
&lt;div class=&quot;divIndentBothSides&quot;&gt;
  &lt;b&gt;WARNING:&lt;/b&gt; You only want to include the crossorigin attribute for files that you know are safe because you are not in control of the server that they&#39;re coming from.
&lt;/div&gt;


&lt;br /&gt;
As shown in the following code snippet, the &lt;i&gt;body &lt;/i&gt;tag will be given an &lt;i&gt;onload&lt;/i&gt; attribute so that the function you specify, &lt;i&gt;initializePage&lt;/i&gt; in this case, will be called when the page first loads. You&#39;ll use this function to wire up an event handler so that you can respond when the user selects a file.
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &amp;lt;body onload=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;initializePage()&quot;&lt;/span&gt;&amp;gt;
&lt;/div&gt;

&lt;br /&gt;
For the file upload control, you&#39;ll use the &lt;i&gt;input&lt;/i&gt; tag with the type &lt;i&gt;file&lt;/i&gt;. Rather than the standard file upload control with a browse button and label indicating which file was selected, as shown below, you&#39;ll wrap the control in a label styled as a button and hide the input control.
  
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjP-TwvbvJdzdCWS1W4FXOmN7y4_7gpSMXiAFqm1cmvyE3mpgN4Pzx5v8epCjXT4Le3jmsZdT8PlwNOu8RSg4e8FD_ivthhaTgb9DMKSbtQZFS_5vF9GeDjenn5_vT2Uq9kb84ZnfJS900/s304/4+-+Normal+FF+File+Open+control.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
    &lt;img border=&quot;0&quot; data-original-height=&quot;35&quot; data-original-width=&quot;304&quot; height=&quot;22&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjP-TwvbvJdzdCWS1W4FXOmN7y4_7gpSMXiAFqm1cmvyE3mpgN4Pzx5v8epCjXT4Le3jmsZdT8PlwNOu8RSg4e8FD_ivthhaTgb9DMKSbtQZFS_5vF9GeDjenn5_vT2Uq9kb84ZnfJS900/w194-h22/4+-+Normal+FF+File+Open+control.png&quot; width=&quot;194&quot; /&gt;
  &lt;/a&gt;
&lt;/div&gt;
  
&lt;br /&gt;
Note that hiding the input control, wrapping it in a label, and styling the label as a button is optional. The file upload will work just fine if you don&#39;t make any changes to the input control so long as the input control is of type &lt;i&gt;file&lt;/i&gt;. 

&lt;br /&gt;&lt;br /&gt;
You&#39;ll also include the &lt;i&gt;accept&lt;/i&gt; attribute for the &lt;i&gt;input&lt;/i&gt; tag to ensure only image files are selected. The upload button&#39;s code is shown in the following snippet:
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &amp;lt;label class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;btn btn-primary btn-file&quot;&lt;/span&gt;&amp;gt;
  &lt;div class=&quot;divIndent&quot;&gt;
    Upload &amp;lt;input id=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;fileUpload&quot;&lt;/span&gt; type=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;file&quot;&lt;/span&gt; accept=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;image/*&quot;&lt;/span&gt; style=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;display:none;&quot;&lt;/span&gt; /&amp;gt;
  &lt;/div&gt;
  &amp;lt;/label&amp;gt;
&lt;/div&gt;

&lt;br /&gt;
Your web page will have four &lt;i&gt;canvas&lt;/i&gt; tags. The canvas tag allows you to draw 2D or 3D graphics on your web page and can even be used for animations. For this article, you&#39;ll use it to display the selected image on the first canvas and then the modified images on the other three canvasses. If you&#39;d like to learn more about the canvas tag, you can visit the following web page: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas&quot; target=&quot;_blank&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas&lt;/a&gt;

&lt;br /&gt;&lt;br /&gt;
Finally, the HTML for the web page will end with two JavaScript file links. The first JavaScript file, &lt;i&gt;pthreads.js&lt;/i&gt;, you&#39;ll create in a moment. The other JavaScript file will be created by Emscripten at the same time as it creates the WebAssembly module. That file handles loading in the WebAssembly module for you, has a number of helper functions to make working with the module easier, and supports various features that might have been enabled when the module was compiled.

&lt;br /&gt;&lt;br /&gt;
Create a file called &lt;i&gt;pthreads.html&lt;/i&gt;, copy the following HTML into it, and then save the file:

&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &amp;lt;html&amp;gt;
  &lt;div class=&quot;divIndent&quot;&gt;
    &amp;lt;head&amp;gt;
    &lt;div class=&quot;divIndent&quot;&gt;
      &amp;lt;title&amp;gt;Pthreads in Firefox&amp;lt;/title&amp;gt;
      &lt;br /&gt;&amp;lt;meta charset=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&amp;gt;&amp;lt;/meta&amp;gt;
      &lt;br /&gt;&amp;lt;meta content=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;width=device-width, initial-scale=1&quot;&lt;/span&gt; name=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;viewport&quot;&lt;/span&gt;&amp;gt;&amp;lt;/meta&amp;gt;
      &lt;br /&gt;&amp;lt;link rel=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt; href=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;https://maxcdn.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css&quot;&lt;/span&gt; crossorigin&amp;gt;&amp;lt;/link&amp;gt;
      &lt;br /&gt;&amp;lt;script src=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js&quot;&lt;/span&gt; crossorigin&amp;gt;&amp;lt;/script&amp;gt;
      &lt;br /&gt;&amp;lt;script src=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js&quot;&lt;/span&gt; crossorigin&amp;gt;&amp;lt;/script&amp;gt;
      &lt;br /&gt;&amp;lt;script src=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;https://maxcdn.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js&quot;&lt;/span&gt; crossorigin&amp;gt;&amp;lt;/script&amp;gt;
    &lt;/div&gt;
    &amp;lt;/head&amp;gt;
    &lt;br /&gt;&amp;lt;body onload=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;initializePage()&quot;&lt;/span&gt;&amp;gt;
    &lt;div class=&quot;divIndent&quot;&gt;
      &amp;lt;div class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;d-flex flex-column&quot;&lt;/span&gt;&amp;gt;
      &lt;div class=&quot;divIndent&quot;&gt;
        &lt;span style=&quot;color:darkgreen;&quot;&gt;&amp;lt;!-- File upload button --&amp;gt;&lt;/span&gt;
        &lt;br /&gt;&amp;lt;div class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;p-2&quot;&lt;/span&gt;&amp;gt;
        &lt;div class=&quot;divIndent&quot;&gt;
          &amp;lt;label class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;btn btn-primary btn-file&quot;&lt;/span&gt;&amp;gt;
          &lt;div class=&quot;divIndent&quot;&gt;
            Upload &amp;lt;input id=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;fileUpload&quot;&lt;/span&gt; type=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;file&quot;&lt;/span&gt; accept=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;image/*&quot;&lt;/span&gt; style=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;display:none;&quot;&lt;/span&gt; /&amp;gt;
          &lt;/div&gt;
          &amp;lt;/label&amp;gt;
        &lt;/div&gt;
        &amp;lt;/div&amp;gt;

        &lt;br /&gt;&lt;br /&gt;&amp;lt;div class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;d-flex flex-wrap&quot;&lt;/span&gt;&amp;gt;
        &lt;div class=&quot;divIndent&quot;&gt;
          &lt;span style=&quot;color:darkgreen;&quot;&gt;&amp;lt;!-- Original image --&amp;gt;&lt;/span&gt;
          &lt;br /&gt;&amp;lt;div class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;p-2 canvasContainer&quot;&lt;/span&gt;&amp;gt;
          &lt;div class=&quot;divIndent&quot;&gt;
            &amp;lt;canvas id=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;originalCanvas&quot;&lt;/span&gt; class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;border rounded&quot;&lt;/span&gt; width=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;250&quot;&lt;/span&gt; height=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;250&quot;&lt;/span&gt;&amp;gt;&amp;lt;/canvas&amp;gt;
            &lt;br /&gt;&amp;lt;div class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;font-weight-bold&quot;&lt;/span&gt;&amp;gt;Original&amp;lt;/div&amp;gt;
            &lt;br /&gt;&amp;lt;div class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;font-weight-light&quot;&lt;/span&gt; id=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;originalImageDimensions&quot;&lt;/span&gt;&amp;gt;&amp;lt;/div&amp;gt;
          &lt;/div&gt;
          &amp;lt;/div&amp;gt;

          &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;&amp;lt;!-- The modified versions of the image --&amp;gt;&lt;/span&gt;
          &lt;br /&gt;&amp;lt;div class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;p-2 canvasContainer&quot;&lt;/span&gt;&amp;gt;
          &lt;div class=&quot;divIndent&quot;&gt;
            &amp;lt;canvas id=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;nonThreadedJSCanvas&quot;&lt;/span&gt; class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;border rounded&quot;&lt;/span&gt; width=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;250&quot;&lt;/span&gt; height=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;250&quot;&lt;/span&gt;&amp;gt;&amp;lt;/canvas&amp;gt;
            &lt;br /&gt;&amp;lt;div class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;font-weight-bold&quot;&lt;/span&gt;&amp;gt;JS - Non-Threaded&amp;lt;/div&amp;gt;
            &lt;br /&gt;&amp;lt;div class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;font-weight-light&quot;&lt;/span&gt; id=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;nonThreadedJSCanvasDuration&quot;&lt;/span&gt;&amp;gt;&amp;lt;/div&amp;gt;
          &lt;/div&gt;
          &amp;lt;/div&amp;gt;

          &lt;br /&gt;&lt;br /&gt;&amp;lt;div class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;p-2 canvasContainer&quot;&lt;/span&gt;&amp;gt;
          &lt;div class=&quot;divIndent&quot;&gt;
            &amp;lt;canvas id=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;nonThreadedWasmCanvas&quot;&lt;/span&gt; class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;border rounded&quot;&lt;/span&gt; width=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;250&quot;&lt;/span&gt; height=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;250&quot;&lt;/span&gt;&amp;gt;&amp;lt;/canvas&amp;gt;
            &lt;br /&gt;&amp;lt;div class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;font-weight-bold&quot;&lt;/span&gt;&amp;gt;Wasm - Non-Threaded&amp;lt;/div&amp;gt;
            &lt;br /&gt;&amp;lt;div class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;font-weight-light&quot;&lt;/span&gt; id=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;nonThreadedWasmCanvasDuration&quot;&lt;/span&gt;&amp;gt;&amp;lt;/div&amp;gt;
          &lt;/div&gt;
          &amp;lt;/div&amp;gt;

          &lt;br /&gt;&lt;br /&gt;&amp;lt;div class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;p-2 canvasContainer&quot;&lt;/span&gt;&amp;gt;
          &lt;div class=&quot;divIndent&quot;&gt;
            &amp;lt;canvas id=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;threadedWasmCanvas&quot;&lt;/span&gt; class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;border rounded&quot;&lt;/span&gt; width=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;250&quot;&lt;/span&gt; height=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;250&quot;&lt;/span&gt;&amp;gt;&amp;lt;/canvas&amp;gt;
            &lt;br /&gt;&amp;lt;div class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;font-weight-bold&quot;&lt;/span&gt;&amp;gt;Wasm - Threaded&amp;lt;/div&amp;gt;
            &lt;br /&gt;&amp;lt;div class=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;font-weight-light&quot;&lt;/span&gt; id=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;threadedWasmCanvasDuration&quot;&lt;/span&gt;&amp;gt;&amp;lt;/div&amp;gt;
          &lt;/div&gt;
          &amp;lt;/div&amp;gt;
        &lt;/div&gt;
        &amp;lt;/div&amp;gt;
      &lt;/div&gt;
      &amp;lt;/div&amp;gt;

      &lt;br /&gt;&lt;br /&gt;&amp;lt;script src=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;js/pthreads.js&quot;&lt;/span&gt;&amp;gt;&amp;lt;/script&amp;gt;
      &lt;br /&gt;&amp;lt;script src=&lt;span style=&quot;color:maroon;&quot;&gt;&quot;js/emscripten_pthread.js&quot;&lt;/span&gt;&amp;gt;&amp;lt;/script&amp;gt;
    &lt;/div&gt;
    &amp;lt;/body&amp;gt;
  &lt;/div&gt;
  &amp;lt;/html&amp;gt;
&lt;/div&gt;
  
&lt;br /&gt;
Now that you&#39;ve created the web page, you need to write the JavaScript that responds to the user choosing a file.


  
&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;Create the JavaScript to load an image from your user&#39;s device&lt;/div&gt;

&lt;br /&gt;
In your &lt;i&gt;frontend&lt;/i&gt; folder, create a &lt;i&gt;js&lt;/i&gt; folder.
  
&lt;br /&gt;&lt;br /&gt;
In the &lt;i&gt;js&lt;/i&gt; folder, create a file called &lt;i&gt;pthreads.js&lt;/i&gt; and open it with your favorite editor.

&lt;br /&gt;&lt;br /&gt;
The first thing you need to do is create the &lt;i&gt;initializePage&lt;/i&gt; function that will be called when your web page loads. In this function, you&#39;ll attach to the file input control&#39;s &lt;i&gt;change&lt;/i&gt; event so that when the user chooses a file, your &lt;i&gt;processImageFile&lt;/i&gt; function will be called. Add the following code snippet to your &lt;i&gt;pthreads.js&lt;/i&gt; file:

&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;function&lt;/span&gt; initializePage() {
  &lt;div class=&quot;divIndent&quot;&gt;
    $(&lt;span style=&quot;color: maroon;&quot;&gt;&quot;#fileUpload&quot;&lt;/span&gt;).on(&lt;span style=&quot;color: maroon;&quot;&gt;&quot;change&quot;&lt;/span&gt;, processImageFile);
  &lt;/div&gt;
  }
&lt;/div&gt;

&lt;br /&gt;
Next you need to define the &lt;i&gt;processImageFile&lt;/i&gt; function. You&#39;ll create a &lt;i&gt;FileReader&lt;/i&gt; object to read in the selected file as a data URL. Once the file&#39;s contents have been loaded, you&#39;ll pass the data URL that was generated to the &lt;i&gt;renderOriginalImage&lt;/i&gt; function. Add the contents of the following code snippet to your &lt;i&gt;pthreads.js&lt;/i&gt; file after the &lt;i&gt;initializePage&lt;/i&gt; function:
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;function&lt;/span&gt; processImageFile(e) {
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; reader = &lt;span style=&quot;color: blue;&quot;&gt;new&lt;/span&gt; FileReader();
    &lt;br /&gt;reader.onload = e =&amp;gt; {
    &lt;div class=&quot;divIndent&quot;&gt;
      renderOriginalImage(e.target.result);
    &lt;/div&gt;
    }
    &lt;br /&gt;reader.readAsDataURL(e.currentTarget.files[0]);
  &lt;/div&gt;
  }
&lt;/div&gt;

&lt;br /&gt;
  The next function that you&#39;re going to create is &lt;i&gt;renderOriginalImage&lt;/i&gt;. This function will first determine the scale needed to draw the user-selected image onto the canvas so that fits within the 250x250 pixel dimensions. It will then call the &lt;i&gt;renderImage&lt;/i&gt; function to display the image on the canvas and then it&#39;ll display the dimensions of the image below the canvas.

&lt;br /&gt;&lt;br /&gt;
Because the image is being drawn to the canvas at 250x250 pixels, you&#39;ll create a temporary canvas object in order to draw the image at its full size. You&#39;ll pull the pixel data from the temporary canvas and pass that off to be adjusted and displayed on the other canvasses.

&lt;br /&gt;&lt;br /&gt;
The full version of the &lt;i&gt;renderOriginalImage&lt;/i&gt; function will be shown in a moment but first, the aspects of the function&#39;s code will be explained.

&lt;br /&gt;&lt;br /&gt;
As shown in the following snippet, the first step to drawing the image onto the canvas is to create an instance of an &lt;i&gt;Image&lt;/i&gt; object and have it load the data URL by setting the &lt;i&gt;src&lt;/i&gt; property. You then respond to the &lt;i&gt;onload&lt;/i&gt; event:
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;function&lt;/span&gt; renderOriginalImage(url) {
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; originalImage = &lt;span style=&quot;color: blue;&quot;&gt;new&lt;/span&gt; Image();
    &lt;br /&gt;originalImage.onload = () =&amp;gt; {
    &lt;div class=&quot;divIndent&quot;&gt;
      &lt;span style=&quot;color: darkgreen;&quot;&gt;// you&#39;ll draw to the canvas here&lt;/span&gt;
    &lt;/div&gt;
    }
    &lt;br /&gt;originalImage.src = url;
  &lt;/div&gt;
  }
&lt;/div&gt;

&lt;br /&gt;
Within the &lt;i&gt;onload&lt;/i&gt; event, you&#39;ll first determine the scale needed for the image so that if fits within the canvas. If the scale is greater than 1.0 then the user-selected image is smaller than the canvas and you&#39;ll leave the scale at 1 so that it gets drawn at its original size.

&lt;br /&gt;&lt;br /&gt;
Next, you&#39;ll place the details about the image size, and scale to draw it, into an object that you&#39;ll name &lt;i&gt;sizeDetails&lt;/i&gt;. You&#39;ll pass the original canvas, image, and size details to the &lt;i&gt;renderImage&lt;/i&gt; function to have the image drawn to the original canvas.

&lt;br /&gt;&lt;br /&gt;
Finally, you&#39;ll display the dimensions of the image below the canvas as shown in the following snippet:
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  …

  &lt;br /&gt;&lt;br /&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; width = originalImage.width;
  &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; height = originalImage.height;
  &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; originalCanvas = $(&lt;span style=&quot;color: maroon;&quot;&gt;&quot;#originalCanvas&quot;&lt;/span&gt;)[0];
  &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;let&lt;/span&gt; scale = Math.min(originalCanvas.width / width, originalCanvas.height / height);
    
  &lt;br /&gt;&lt;br /&gt;
  &lt;span style=&quot;color: darkgreen;&quot;&gt;// If the image is smaller than the canvas, draw at its original size&lt;/span&gt;
  &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;if&lt;/span&gt; (scale &amp;gt; 1.0) { scale = 1; }

  &lt;br /&gt;&lt;br /&gt;
  &lt;span style=&quot;color: darkgreen;&quot;&gt;// Render the image to the canvas&lt;/span&gt;
  &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; sizeDetails = { width: width, height: height, scale: scale };
  &lt;br /&gt;renderImage(originalCanvas, originalImage, sizeDetails);

  &lt;br /&gt;&lt;br /&gt;
  &lt;span style=&quot;color: darkgreen;&quot;&gt;// Display the dimensions&lt;/span&gt;
  &lt;br /&gt;$(&lt;span style=&quot;color: maroon;&quot;&gt;&quot;#originalImageDimensions&quot;&lt;/span&gt;).text(&lt;span style=&quot;color: maroon;&quot;&gt;`Dimensions: ${width} x ${height}`&lt;/span&gt;);

  &lt;br /&gt;&lt;br /&gt;
  …
&lt;/div&gt;
  

&lt;br /&gt;
Your next step is to create a temporary canvas to draw the original image on at its full size as shown in the following snippet:

&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  …
  
  &lt;br /&gt;&lt;br /&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; $canvas = $(&lt;span style=&quot;color: maroon;&quot;&gt;&quot;&amp;lt;canvas /&amp;gt;&quot;&lt;/span&gt;);
  &lt;br /&gt;$canvas.prop({ width: width, height: height });
  &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; canvasContext = $canvas[0].getContext(&lt;span style=&quot;color: maroon;&quot;&gt;&quot;2d&quot;&lt;/span&gt;);
  &lt;br /&gt;canvasContext.drawImage(originalImage, 0, 0, width, height);

  &lt;br /&gt;&lt;br /&gt;
  …
&lt;/div&gt;
  
  
&lt;br /&gt;
The final portion of code within the &lt;i&gt;onload&lt;/i&gt; event of the Image instance is shown in the following code snippet. The code will grab the pixel data from the temporary canvas using the context&#39;s &lt;i&gt;getImageData&lt;/i&gt; function and will pass that off to the &lt;i&gt;adjustImageJS&lt;/i&gt; and &lt;i&gt;adjustImageWasm&lt;/i&gt; functions to modify and display the results.

&lt;br /&gt;&lt;br /&gt;
One thing to note about the following code snippet is that the &lt;i&gt;adjustImageJS&lt;/i&gt; and &lt;i&gt;adjustImageWasm&lt;/i&gt; functions are asynchronous and will finish at some point after the &lt;i&gt;onload&lt;/i&gt; event completes. The functions are asynchronous so that the JavaScript code isn&#39;t blocking the browser while the modifications are being made. All three functions will execute at the same time and the canvasses that are ready will be drawn when the data is received rather than in the sequence that the functions were called. The browser will also remain responsive to user input.

&lt;br /&gt;&lt;br /&gt;
If you did want to wait for the functions to complete before exiting the onload event, you can pass the result of each function to a variable (for example: &lt;i&gt;const promise1 = functionCall();&lt;/i&gt;). Using this approach will allow each function to execute concurrently and then you can await the variables (for example: &lt;i&gt;await promise1;&lt;/i&gt;). The following web page has more information on the &lt;i&gt;async&lt;/i&gt; and &lt;i&gt;await&lt;/i&gt; keywords if you&#39;d like to learn more: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await&quot; target=&quot;_blank&quot;&gt;https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await&lt;/a&gt;

&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  ...

  &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; originalImageData = canvasContext.getImageData(0, 0, width, height);
    
  &lt;br /&gt;adjustImageJS(originalImageData, sizeDetails, &lt;span style=&quot;color: maroon;&quot;&gt;&quot;nonThreadedJSCanvas&quot;&lt;/span&gt;);
    
  &lt;br /&gt;adjustImageWasm(originalImageData, sizeDetails, &lt;span style=&quot;color: maroon;&quot;&gt;&quot;nonThreadedWasmCanvas&quot;&lt;/span&gt;);
    
  &lt;br /&gt;adjustImageWasm(originalImageData, sizeDetails, &lt;span style=&quot;color: maroon;&quot;&gt;&quot;threadedWasmCanvas&quot;&lt;/span&gt;);
  
  
  &lt;br /&gt;&lt;br /&gt;...
&lt;/div&gt;
  
&lt;br /&gt;
The full &lt;i&gt;renderOriginalImage&lt;/i&gt; function is shown below. Add it after the &lt;i&gt;processImageFile&lt;/i&gt; function in your &lt;i&gt;pthreads.js&lt;/i&gt; file:
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;function&lt;/span&gt; renderOriginalImage(url) {
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; originalImage = &lt;span style=&quot;color: blue;&quot;&gt;new&lt;/span&gt; Image();
    &lt;br /&gt;originalImage.onload = () =&amp;gt; {
    &lt;div class=&quot;divIndent&quot;&gt;
      &lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; width = originalImage.width;
      &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; height = originalImage.height;
      &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; originalCanvas = $(&lt;span style=&quot;color: maroon;&quot;&gt;&quot;#originalCanvas&quot;&lt;/span&gt;)[0];
      &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;let&lt;/span&gt; scale = Math.min(originalCanvas.width / width, originalCanvas.height / height);
    
      &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// If the image is smaller than the canvas, draw at its original size&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;if&lt;/span&gt; (scale &amp;gt; 1.0) { scale = 1; }

      &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// Render the image to the canvas&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; sizeDetails = { width: width, height: height, scale: scale };
      &lt;br /&gt;renderImage(originalCanvas, originalImage, sizeDetails);

      &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// Display the dimensions&lt;/span&gt;
      &lt;br /&gt;$(&lt;span style=&quot;color: maroon;&quot;&gt;&quot;#originalImageDimensions&quot;&lt;/span&gt;).text(&lt;span style=&quot;color: maroon;&quot;&gt;`Dimensions: ${width} x ${height}`&lt;/span&gt;);
      
      &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// Create a temporary canvas and draw the image at its full size.&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; $canvas = $(&lt;span style=&quot;color: maroon;&quot;&gt;&quot;&amp;lt;canvas /&amp;gt;&quot;&lt;/span&gt;);
      &lt;br /&gt;$canvas.prop({ width: width, height: height });
      &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; canvasContext = $canvas[0].getContext(&lt;span style=&quot;color: maroon;&quot;&gt;&quot;2d&quot;&lt;/span&gt;);
      &lt;br /&gt;canvasContext.drawImage(originalImage, 0, 0, width, height);
      
      &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// Grab the image data from the temporary canvas, have the data modified by the&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// JavaScript code and WebAssembly module, and then render the modified&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// images. Note that adjustImageJS and adjustImageWasm are async.&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; originalImageData = canvasContext.getImageData(0, 0, width, height);
      &lt;br /&gt;adjustImageJS(originalImageData, sizeDetails, &lt;span style=&quot;color: maroon;&quot;&gt;&quot;nonThreadedJSCanvas&quot;&lt;/span&gt;);
      &lt;br /&gt;adjustImageWasm(originalImageData, sizeDetails, &lt;span style=&quot;color: maroon;&quot;&gt;&quot;nonThreadedWasmCanvas&quot;&lt;/span&gt;);
      &lt;br /&gt;adjustImageWasm(originalImageData, sizeDetails, &lt;span style=&quot;color: maroon;&quot;&gt;&quot;threadedWasmCanvas&quot;&lt;/span&gt;);
      
    &lt;/div&gt;
    }
    &lt;br /&gt;originalImage.src = url;
  &lt;/div&gt;
  }
&lt;/div&gt;

  
&lt;br /&gt;
After the &lt;i&gt;renderOriginalImage&lt;/i&gt; function, you&#39;ll need to create the &lt;i&gt;renderImage&lt;/i&gt; function. The function receives a canvas to draw onto, the image source to draw, and the details about the image size and scale.

&lt;br /&gt;&lt;br /&gt;
The function starts out by clearing the canvas of anything that might already be there if this isn&#39;t the first time the user selected an image. Next, the scale of the canvas is adjusted to the scale specified in the &lt;i&gt;sizeDetails&lt;/i&gt; object. The image is then drawn to the canvas.

&lt;br /&gt;&lt;br /&gt;
Before the function exits, it resets the scale of the canvas back to its original values by calling the &lt;i&gt;setTransform&lt;/i&gt; function on the context.

&lt;br /&gt;&lt;br /&gt;
Add the &lt;i&gt;renderImage&lt;/i&gt; function, shown in the following snippet, after your &lt;i&gt;renderOriginalImage&lt;/i&gt; function:

&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;function&lt;/span&gt; renderImage(canvas, imageSource, sizeDetails) {
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; context = canvas.getContext(&lt;span style=&quot;color: maroon;&quot;&gt;&quot;2d&quot;&lt;/span&gt;);
    &lt;br /&gt;context.clearRect(0, 0, 250, 250);
    &lt;br /&gt;context.scale(sizeDetails.scale, sizeDetails.scale);    
    &lt;br /&gt;context.drawImage(imageSource, 0, 0, sizeDetails.width, sizeDetails.height);
    &lt;br /&gt;context.setTransform(1, 0, 0, 1, 0, 0);
  &lt;/div&gt;
}
&lt;/div&gt;
  
&lt;br /&gt;
Now that you&#39;re able to display the image that the user selects, the next step is shown in the following image where you&#39;ll adjust the image data and display the results using only JavaScript. This will give you a comparison to see what the difference is between the JavaScript approach and the two WebAssembly approaches.
  
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrKBXx_P9CNwLzLIwkXpmo4oYHuM1wsixcfcdZyxQOcSvx0fbu4LJrXQyR_-uNZd1hj6kK1wZf0bovBOSxiYtEHNNl3oCkJqNyYZ0wo9a4QcRrjE6y8or2j5L41FcXiHVgRg4Z6gidUWM/s1608/5+-+the+3rd+step.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
    &lt;img border=&quot;0&quot; data-original-height=&quot;788&quot; data-original-width=&quot;1608&quot; height=&quot;196&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrKBXx_P9CNwLzLIwkXpmo4oYHuM1wsixcfcdZyxQOcSvx0fbu4LJrXQyR_-uNZd1hj6kK1wZf0bovBOSxiYtEHNNl3oCkJqNyYZ0wo9a4QcRrjE6y8or2j5L41FcXiHVgRg4Z6gidUWM/w400-h196/5+-+the+3rd+step.png&quot; width=&quot;400&quot; /&gt;
  &lt;/a&gt;
  &lt;br /&gt;(click to view the image full size)
&lt;/div&gt;
  

  
&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;3. Adjusting the image using JavaScript&lt;/div&gt;
  
&lt;br /&gt;
The &lt;i&gt;renderOriginalImage&lt;/i&gt; function that you created calls the &lt;i&gt;adjustImageJS&lt;/i&gt; function to have the user&#39;s selected image adjusted using JavaScript. In the &lt;i&gt;adjustImageJS&lt;/i&gt; function, you&#39;ll create a copy of the original image data using the a &lt;i&gt;Uint8ClampedArray&lt;/i&gt; which ensures each value is an integer in the range of &lt;i&gt;0&lt;/i&gt; to &lt;i&gt;255&lt;/i&gt;. If a value is not an integer, it&#39;s rounded to the nearest integer. More information on this array can be found here if you&#39;re interested: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray&quot; target=&quot;_blank&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray&lt;/a&gt;

&lt;br /&gt;&lt;br /&gt;
Once you have a copy of the image data, you&#39;ll pass that off to the &lt;i&gt;adjustPixels&lt;/i&gt; function telling it to loop from the first pixel to the last. The function will adjust the pixels in the Uint8ClampedArray instance that you pass in.
  
&lt;br /&gt;&lt;br /&gt;
Before and after the &lt;i&gt;adjustPixels&lt;/i&gt; call, you&#39;ll grab the current date and time to determine how long the function takes to execute.

&lt;br /&gt;&lt;br /&gt;
Finally, you&#39;ll call the &lt;i&gt;renderModifiedImage&lt;/i&gt; function to have the modified pixels rendered on the desired canvas. 

&lt;br /&gt;&lt;br /&gt;
Add the &lt;i&gt;adjustImageJS&lt;/i&gt; function shown in the following code snippet after the &lt;i&gt;renderOriginalImage&lt;/i&gt; function in your &lt;i&gt;pthreads.js&lt;/i&gt; file:
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;async function&lt;/span&gt; adjustImageJS(imageData, sizeDetails, destinationCanvasId) {
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color: darkgreen;&quot;&gt;// Get a copy of the imageData and the number of bytes it contains.&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; imageDataBytes = Uint8ClampedArray.from(imageData.data);
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; bufferSize = imageDataBytes.byteLength;

    &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// Adjust the pixels using JavaScript and get the duration&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; Start = new Date();
    &lt;br /&gt;adjustPixels(imageDataBytes, 0, bufferSize);
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; duration = (new Date() - Start);
    &lt;br /&gt;console.log(&lt;span style=&quot;color: maroon;&quot;&gt;`JavaScript version took ${duration} milliseconds to execute.`&lt;/span&gt;); 

    &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// Have the modified image displayed&lt;/span&gt;
    &lt;br /&gt;renderModifiedImage(destinationCanvasId, imageDataBytes, sizeDetails, duration);
  &lt;/div&gt;
  }
&lt;/div&gt;

&lt;br /&gt;
Each pixel in the image data has four bytes (one for each color and the alpha channel). The &lt;i&gt;adjustPixels&lt;/i&gt; function will loop from the first index specified to one less than the last index specified and will step through the data in increments of four. Each time through the loop, the &lt;i&gt;adjustColors&lt;/i&gt; function is called to adjust the colors at that index.

&lt;br /&gt;&lt;br /&gt;
Add the &lt;i&gt;adjustPixels&lt;/i&gt; function, that&#39;s shown in the following snippet, after the &lt;i&gt;adjustImageJS&lt;/i&gt; function in your &lt;i&gt;pthreads.js&lt;/i&gt; file:

&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;function&lt;/span&gt; adjustPixels(imageData, startIndex, stopIndex) {
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color: darkgreen;&quot;&gt;// Loop through every fourth byte because adjustColors operates on 4&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// bytes at a time (RGBA data)&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;for&lt;/span&gt; (&lt;span style=&quot;color: blue;&quot;&gt;let&lt;/span&gt; index = startIndex; index &amp;lt; stopIndex; index += 4) {
    &lt;div class=&quot;divIndent&quot;&gt;
      adjustColors(imageData, index);
    &lt;/div&gt;
    }
  &lt;/div&gt;
  }
&lt;/div&gt;
  
&lt;br /&gt;
The &lt;i&gt;adjustColors&lt;/i&gt; function grabs the Red, Green, and Blue values and averages them out. Then it applies the calculated color to the Red, Green, and Blue values to create the grey. The alpha channel isn&#39;t adjusted.

&lt;br /&gt;&lt;br /&gt;
Add the &lt;i&gt;adjustColors&lt;/i&gt; function, from the following code snippet, after the &lt;i&gt;adjustPixels&lt;/i&gt; function in your &lt;i&gt;pthreads.js&lt;/i&gt; file.
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;function&lt;/span&gt; adjustColors(imageData, index) {
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color: darkgreen;&quot;&gt;// Average out the colors&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; newColor = ((imageData[index] + imageData[index + 1] + 
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;imageData[index + 2]) / 3); 

    &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// Set each channel&#39;s value to the new value to make the grey&lt;/span&gt;
    &lt;br /&gt;imageData[index] = newColor; &lt;span style=&quot;color: darkgreen;&quot;&gt;// Red&lt;/span&gt;
    &lt;br /&gt;imageData[index + 1] = newColor; &lt;span style=&quot;color: darkgreen;&quot;&gt;// Green&lt;/span&gt;
    &lt;br /&gt;imageData[index + 2] = newColor; &lt;span style=&quot;color: darkgreen;&quot;&gt;// Blue&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// no need to adjust the Alpha channel value&lt;/span&gt;
  &lt;/div&gt;
  }
&lt;/div&gt;

 
&lt;br /&gt;
Now, to have the modified image data rendered to a canvas, you&#39;ll create the &lt;i&gt;renderModifiedImage&lt;/i&gt; function. 

&lt;br /&gt;&lt;br /&gt;
You&#39;ll want the modified image displayed to the target canvas at the scale needed so that it fits within the canvas. To do this, you&#39;ll need to create a temporary canvas at the original image size and then get the image data from that canvas. You then overwrite the image data with the modified data and put that new image data back into the temporary canvas to have it drawn.

&lt;br /&gt;&lt;br /&gt;
Next, you&#39;ll call the &lt;i&gt;renderImage&lt;/i&gt; function passing in the destination canvas that the image will be drawn to, the temporary canvas as the image source, and the size details of the image.

&lt;br /&gt;&lt;br /&gt;
Lastly, the function will display how long the calling code took to execute the modifications.

&lt;br /&gt;&lt;br /&gt;
Add the &lt;i&gt;renderModifiedImage&lt;/i&gt; function, shown in the following snippet, after the &lt;i&gt;adjustColors&lt;/i&gt; function in your &lt;i&gt;pthreads.js&lt;/i&gt; file:
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;function&lt;/span&gt; renderModifiedImage(canvasId, byteArray, sizeDetails, duration) {
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color: darkgreen;&quot;&gt;// Create a temporary canvas that&#39;s the size of the image that was modified&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; $canvas = $(&lt;span style=&quot;color: maroon;&quot;&gt;&quot;&amp;lt;canvas /&amp;gt;&quot;&lt;/span&gt;);
    &lt;br /&gt;$canvas.prop({ width: sizeDetails.width, height: sizeDetails.height });
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; canvas = $canvas[0];
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; canvasContext = canvas.getContext(&quot;2d&quot;);

    &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// Get the image data of the temporary canvas and update it with the modified&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// pixel data.&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; modifiedImageData = canvasContext.getImageData(0, 0, 
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sizeDetails.width, sizeDetails.height);
    &lt;br /&gt;modifiedImageData.data.set(byteArray);
    &lt;br /&gt;canvasContext.putImageData(modifiedImageData, 0, 0);

    &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// Have the temporary canvas drawn onto the destination canvas&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; destinationCanvas = $(&lt;span style=&quot;color: maroon;&quot;&gt;`#${canvasId}`&lt;/span&gt;)[0];
    &lt;br /&gt;renderImage(destinationCanvas, canvas, sizeDetails);

    &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// Indicate how long the code took to run&lt;/span&gt;
    &lt;br /&gt;$(&lt;span style=&quot;color: maroon;&quot;&gt;`#${canvasId}Duration`&lt;/span&gt;).text(&lt;span style=&quot;color: maroon;&quot;&gt;`${duration} milliseconds`&lt;/span&gt;);
  &lt;/div&gt;
  }
&lt;/div&gt;

&lt;br /&gt;
Now that you have the code that adjusts the original image using JavaScript, the last bit of JavaScript code that you need to create is the &lt;i&gt;adjustImageWasm&lt;/i&gt; function. This function will pass the original image data to the WebAssembly module, have the module modify the image, and then retrieve the modified data from the module to be displayed on the desired canvas.

&lt;br /&gt;&lt;br /&gt;
The full version of the &lt;i&gt;adjustImageWasm&lt;/i&gt; function will be shown in a moment. I&#39;ll explain the sections of the function&#39;s code first.

&lt;br /&gt;&lt;br /&gt;
The first thing the function needs to do is allocate a portion of the module&#39;s memory to hold the image data. Then you copy the image data to that location in the module&#39;s memory as shown in the following snippet:
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;async function&lt;/span&gt; adjustImageWasm(imageData, sizeDetails, destinationCanvasId) {
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; bufferSize = imageData.data.byteLength;
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; imageDataPointer = Module._CreateBuffer(bufferSize);
    &lt;br /&gt;Module.HEAPU8.set(imageData.data, 
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(imageDataPointer / Module.HEAPU8.BYTES_PER_ELEMENT));

    &lt;br /&gt;&lt;br /&gt;...
  &lt;/div&gt;
  }
&lt;/div&gt;

&lt;br /&gt;
The next step is to call the desired function based on the &lt;i&gt;destinationCanvasId&lt;/i&gt; parameter that&#39;s passed to the function as shown in the following snippet:
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;async function&lt;/span&gt; adjustImageWasm(imageData, sizeDetails, destinationCanvasId) {
  &lt;div class=&quot;divIndent&quot;&gt;
    ...

    &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// Call the module&#39;s non-threaded function&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;if&lt;/span&gt; (destinationCanvasId === &lt;span style=&quot;color: maroon;&quot;&gt;&quot;nonThreadedWasmCanvas&quot;&lt;/span&gt;) {
    &lt;div class=&quot;divIndent&quot;&gt;
      Module._AdjustImageWithoutUsingThreads(imageDataPointer, bufferSize);
    &lt;/div&gt;
    }
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;else&lt;/span&gt; { &lt;span style=&quot;color: darkgreen;&quot;&gt;// Call the module&#39;s threaded function&lt;/span&gt;
    &lt;div class=&quot;divIndent&quot;&gt;
      Module._AdjustImageUsingThreads(imageDataPointer, bufferSize);
    &lt;/div&gt;
    }

     &lt;br /&gt;&lt;br /&gt;...
  &lt;/div&gt;
  }
&lt;/div&gt;

&lt;br /&gt;
The code copies the modified image data from the module&#39;s memory and then tells the module that it can release the memory that was allocated for the image data as shown in the following snippet:

&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;async function&lt;/span&gt; adjustImageWasm(imageData, sizeDetails, destinationCanvasId) {
  &lt;div class=&quot;divIndent&quot;&gt;
    ...

    &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// Copy the modified bytes from the module&#39;s memory (1st line gets a&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// view of a section of the HEAPU8&#39;s buffer. 2nd line makes a copy of the&lt;/span&gt; 
    &lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// bytes because we&#39;re about to free that part of the module&#39;s memory)&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; byteView = &lt;span style=&quot;color: blue;&quot;&gt;new&lt;/span&gt; Uint8Array(Module.HEAPU8.buffer, imageDataPointer, bufferSize);
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; byteCopy = &lt;span style=&quot;color: blue;&quot;&gt;new&lt;/span&gt; Uint8Array(byteView); &lt;span style=&quot;color: darkgreen;&quot;&gt;// copies when given a typed array&lt;/span&gt;

    &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// Release the memory that was allocated for the image data&lt;/span&gt;
    &lt;br /&gt;Module._FreeBuffer(imageDataPointer);

    &lt;br /&gt;&lt;br /&gt;...
  &lt;/div&gt;
  }
&lt;/div&gt;

&lt;br /&gt;
Finally, the &lt;i&gt;renderModifiedImage&lt;/i&gt; function is called to display the results of the modification to the appropriate canvas.

&lt;br /&gt;&lt;br /&gt;
The following code snippet shows the whole &lt;i&gt;adjustImageWasm&lt;/i&gt; function that you need to place after the &lt;i&gt;renderModifiedImage&lt;/i&gt; function in your &lt;i&gt;pthreads.js&lt;/i&gt; file:
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;async function&lt;/span&gt; adjustImageWasm(imageData, sizeDetails, destinationCanvasId) {
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color: darkgreen;&quot;&gt;// Get the number of bytes in the ImageData&#39;s Uint8ClampedArray and&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// then reserve space in the module&#39;s memory for the image data.&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// Copy the data in.&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; bufferSize = imageData.data.byteLength;
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; imageDataPointer = Module._CreateBuffer(bufferSize);
    &lt;br /&gt;Module.HEAPU8.set(imageData.data, 
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(imageDataPointer / Module.HEAPU8.BYTES_PER_ELEMENT));
  
    &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// Call the module&#39;s non-threaded function&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;if&lt;/span&gt; (destinationCanvasId === &lt;span style=&quot;color: maroon;&quot;&gt;&quot;nonThreadedWasmCanvas&quot;&lt;/span&gt;) {
    &lt;div class=&quot;divIndent&quot;&gt;
      Module._AdjustImageWithoutUsingThreads(imageDataPointer, bufferSize);
    &lt;/div&gt;
    }
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;else&lt;/span&gt; { &lt;span style=&quot;color: darkgreen;&quot;&gt;// Call the module&#39;s threaded function&lt;/span&gt;
    &lt;div class=&quot;divIndent&quot;&gt;
      Module._AdjustImageUsingThreads(imageDataPointer, bufferSize);
    &lt;/div&gt;
    }  

    &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// Copy the modified bytes from the module&#39;s memory (1st line gets a&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// view of a section of the HEAPU8&#39;s buffer. 2nd line makes a copy of&lt;/span&gt; 
    &lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// the bytes because we&#39;re about to free that part of the module&#39;s memory)&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; byteView = &lt;span style=&quot;color: blue;&quot;&gt;new&lt;/span&gt; Uint8Array(Module.HEAPU8.buffer, imageDataPointer, bufferSize);
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;const&lt;/span&gt; byteCopy = &lt;span style=&quot;color: blue;&quot;&gt;new&lt;/span&gt; Uint8Array(byteView); &lt;span style=&quot;color: darkgreen;&quot;&gt;// make a copy&lt;/span&gt;

    &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// Release the memory that was allocated for the image data&lt;/span&gt;
    &lt;br /&gt;Module._FreeBuffer(imageDataPointer);  

    &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// Have the modified image displayed&lt;/span&gt;
    &lt;br /&gt;renderModifiedImage(destinationCanvasId, byteCopy, sizeDetails, Module._GetDuration());
  &lt;/div&gt;
  }
&lt;/div&gt;
    
&lt;br /&gt;
Save the &lt;i&gt;pthreads.js&lt;/i&gt; file.

&lt;br /&gt;&lt;br /&gt;
With the web page now created, your next step as shown in the following image, is to create the WebAssembly module.
 
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWR6aCxGrUHfqvcMHTqCWqcHVQgKQHMmlU-FpJ0TmTuTiyV-dwY2SfwKKAfc-_XoS9qwZ0SlPH4X-4jomnCHmekDfZYqqvJeBgbtHAE77plMR1uaQiQffnlp0OsIczXK6SMd9wwRzffVo/s1608/6+-+the+4th+step.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
    &lt;img border=&quot;0&quot; data-original-height=&quot;788&quot; data-original-width=&quot;1608&quot; height=&quot;196&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWR6aCxGrUHfqvcMHTqCWqcHVQgKQHMmlU-FpJ0TmTuTiyV-dwY2SfwKKAfc-_XoS9qwZ0SlPH4X-4jomnCHmekDfZYqqvJeBgbtHAE77plMR1uaQiQffnlp0OsIczXK6SMd9wwRzffVo/w400-h196/6+-+the+4th+step.png&quot; width=&quot;400&quot; /&gt;
  &lt;/a&gt;
  &lt;br /&gt;(click to view the image full size)
&lt;/div&gt;
  
  
&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;4. Create the WebAssembly module&lt;/div&gt;

&lt;br /&gt;
To create the WebAssembly module, you&#39;re going to write some C++ code and compile it to WebAssembly using Emscripten. 

&lt;br /&gt;&lt;br /&gt;
Create a &lt;i&gt;source&lt;/i&gt; folder that&#39;s at the same level as your &lt;i&gt;frontend&lt;/i&gt; folder. 

&lt;br /&gt;&lt;br /&gt;
In the &lt;i&gt;source&lt;/i&gt; folder create a file called &lt;i&gt;pthreads.cpp&lt;/i&gt; and then open it with your editor.

&lt;br /&gt;&lt;br /&gt;
You&#39;ll start the &lt;i&gt;pthreads.cpp&lt;/i&gt; file with the headers needed for the &lt;i&gt;uint8_t&lt;/i&gt; data type (&lt;i&gt;cstdio&lt;/i&gt;), the &lt;i&gt;std::chrono&lt;/i&gt; library (&lt;i&gt;chrono&lt;/i&gt;) to help track how long the image manipulation takes, &lt;i&gt;pthread.h&lt;/i&gt; for &lt;i&gt;pthread&lt;/i&gt; support, and &lt;i&gt;emscripten.h&lt;/i&gt; for &lt;i&gt;Emscripten&lt;/i&gt; support. You&#39;ll also add an &lt;i&gt;extern &quot;C&quot;&lt;/i&gt; block around the code so that the compiler doesn&#39;t adjust the function names. 

&lt;br /&gt;&lt;br /&gt;
Add the code in the following snippet to your &lt;i&gt;pthreads.cpp&lt;/i&gt; file.
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  #include &lt;span style=&quot;color:maroon;&quot;&gt;&amp;lt;cstdio&amp;gt;&lt;/span&gt; &lt;span style=&quot;color: darkgreen;&quot;&gt;// for uint8_t (emcc also needs C++11: -std=c++11)&lt;/span&gt;
  &lt;br /&gt;#include &lt;span style=&quot;color:maroon;&quot;&gt;&amp;lt;chrono&amp;gt;&lt;/span&gt;
  &lt;br /&gt;#include &lt;span style=&quot;color:maroon;&quot;&gt;&amp;lt;pthread.h&amp;gt;&lt;/span&gt;
  &lt;br /&gt;#include &lt;span style=&quot;color:maroon;&quot;&gt;&amp;lt;emscripten.h&amp;gt;&lt;/span&gt;

  &lt;br /&gt;&lt;br /&gt;#ifdef __cplusplus
  &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;extern&lt;/span&gt; &lt;span style=&quot;color:maroon;&quot;&gt;&quot;C&quot;&lt;/span&gt; { &lt;span style=&quot;color: darkgreen;&quot;&gt;// So that the C++ compiler doesn&#39;t adjust your function names&lt;/span&gt;
  &lt;br /&gt;#endif

  &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// All of your C++ code will go here&lt;/span&gt;

  &lt;br /&gt;&lt;br /&gt;#ifdef __cplusplus
  &lt;br /&gt;}
  &lt;br /&gt;#endif
&lt;/div&gt;

&lt;br /&gt;
Add the following global variable within the &lt;i&gt;extern &quot;C&quot;&lt;/i&gt; block in your &lt;i&gt;pthreads.cpp&lt;/i&gt; file. The variable will be set once execution completes and will be returned when the &lt;i&gt;GetDuration&lt;/i&gt; function is called.

&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;double&lt;/span&gt; execution_duration = 0.0;
&lt;/div&gt;

&lt;br /&gt;
After the &lt;i&gt;execution_duration&lt;/i&gt; global variable, and within the &lt;i&gt;extern &quot;C&quot;&lt;/i&gt; block of your &lt;i&gt;pthreads.cpp&lt;/i&gt; file, add the functions in the following code snippet that will allocate space in the module&#39;s memory and free that memory respectively:
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  EMSCRIPTEN_KEEPALIVE
  &lt;br /&gt;uint8_t* CreateBuffer(&lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt; size)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;return new&lt;/span&gt; uint8_t[size];
  &lt;/div&gt;
  }

  &lt;br /&gt;&lt;br /&gt;EMSCRIPTEN_KEEPALIVE
  &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;void&lt;/span&gt; FreeBuffer(uint8_t* buffer)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;delete&lt;/span&gt;[] buffer;
  &lt;/div&gt;
  }
&lt;/div&gt;

&lt;br /&gt;
After the &lt;i&gt;FreeBuffer&lt;/i&gt; function, and within the &lt;i&gt;extern &quot;C&quot;&lt;/i&gt; block of your &lt;i&gt;pthreads.cpp&lt;/i&gt; file, add the following function that will tell the caller how long it took for the code to execute:
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  EMSCRIPTEN_KEEPALIVE
  &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; GetDuration()
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;return&lt;/span&gt; execution_duration;
  &lt;/div&gt;
  }
&lt;/div&gt;

&lt;br /&gt;
Aside from slight syntax differences, the following two functions are the same as the JavaScript versions you created earlier. The &lt;i&gt;AdjustColors&lt;/i&gt; function adjusts the colors for a specific index and the &lt;i&gt;AdjustPixels&lt;/i&gt; function loops through a range of indexes calling &lt;i&gt;AdjustColors&lt;/i&gt; for every fourth index.

&lt;br /&gt;&lt;br /&gt;
Add the code in the following snippet to your &lt;i&gt;pthreads.cpp&lt;/i&gt; file after the &lt;i&gt;GetDuration&lt;/i&gt; function and within the &lt;i&gt;extern &quot;C&quot;&lt;/i&gt; block:

&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;void&lt;/span&gt; AdjustColors(uint8_t* image_data, &lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt; index)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color: darkgreen;&quot;&gt;// Average out the colors&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt; new_color = ((image_data[index] + image_data[index + 1] + 
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;image_data[index + 2]) / 3); 
  
    &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// Set each channel&#39;s value to the new value to make the grey&lt;/span&gt;
    &lt;br /&gt;image_data[index] = new_color; &lt;span style=&quot;color: darkgreen;&quot;&gt;// Red&lt;/span&gt;
    &lt;br /&gt;image_data[index + 1] = new_color; &lt;span style=&quot;color: darkgreen;&quot;&gt;// Green&lt;/span&gt;
    &lt;br /&gt;image_data[index + 2] = new_color; &lt;span style=&quot;color: darkgreen;&quot;&gt;// Blue&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// no need to adjust the Alpha channel value&lt;/span&gt;
  &lt;/div&gt;
  }

  &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;void&lt;/span&gt; AdjustPixels(uint8_t* image_data, &lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt; start_index, &lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt; stop_index)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color: darkgreen;&quot;&gt;// Loop through every fourth byte because AdjustColors operates on 4&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;// bytes at a time (RGBA data)&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;for&lt;/span&gt; (&lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt; index = start_index; index &amp;lt; stop_index; index += 4)
    &lt;br /&gt;{
    &lt;div class=&quot;divIndent&quot;&gt;
      AdjustColors(image_data, index);
    &lt;/div&gt;
    }
  &lt;/div&gt;
  }
&lt;/div&gt;
                                       
&lt;br /&gt;
The next function that you&#39;ll create is the &lt;i&gt;AdjustImageWithoutUsingThreads&lt;/i&gt; function. This function will grab the current time, call the &lt;i&gt;AdjustPixels&lt;/i&gt; function telling it to modify all the pixels in the image, and then it will grab the current time again in order to calculate the execution&#39;s duration. The duration is then placed in the &lt;i&gt;execution_duration&lt;/i&gt; global variable.

&lt;br /&gt;&lt;br /&gt;
Add the code in the following snippet to your &lt;i&gt;pthreads.cpp&lt;/i&gt; file after the &lt;i&gt;AdjustPixels&lt;/i&gt; function and within the &lt;i&gt;extern &quot;C&quot;&lt;/i&gt; block:

&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  EMSCRIPTEN_KEEPALIVE
  &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;void&lt;/span&gt; AdjustImageWithoutUsingThreads(uint8_t* image_data, &lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt; image_data_size)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:darkgreen;&quot;&gt;// Not using &#39;clock_t start = clock()&#39; because that returns the CPU clock&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// which includes how much CPU time each thread uses too. We want&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// to know the wall clock time that has passed.&lt;/span&gt;
    &lt;br /&gt;std::chrono::high_resolution_clock::time_point duration_start = 
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::chrono::high_resolution_clock::now();

    &lt;br /&gt;&lt;br /&gt;AdjustPixels(image_data, 0, image_data_size);

    &lt;br /&gt;&lt;br /&gt;std::chrono::high_resolution_clock::time_point duration_end = 
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::chrono::high_resolution_clock::now();
    &lt;br /&gt;std::chrono::duration&amp;lt;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; std::milli&amp;gt; duration = 
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(duration_end - duration_start);

    &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// Convert the value into a normal double&lt;/span&gt;
    &lt;br /&gt;execution_duration = duration.count();

    &lt;br /&gt;&lt;br /&gt;printf(&lt;span style=&quot;color:maroon;&quot;&gt;&quot;AdjustImageWithoutUsingThreads took %f milliseconds to execute.\n&quot;&lt;/span&gt;,
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;duration.count());
  &lt;/div&gt;
  }
&lt;/div&gt;
  
&lt;br /&gt;
Your next step is to define an object (&lt;i&gt;thread_args&lt;/i&gt;) that you&#39;ll use to pass information to the threads that you create. This will hold a pointer to the image data, the index for where to start adjusting the image, and an index for where to stop.

&lt;br /&gt;&lt;br /&gt;
Following the definition of the &lt;i&gt;thread_args&lt;/i&gt; object, you&#39;ll create the thread function itself (&lt;i&gt;thread_func&lt;/i&gt;). The &lt;i&gt;thread_func&lt;/i&gt; function will call the &lt;i&gt;AdjustPixels&lt;/i&gt; function passing it the values it receives from the &lt;i&gt;thread_args&lt;/i&gt; parameter value.

&lt;br /&gt;&lt;br /&gt;
After your &lt;i&gt;AdjustImageWithoutUsingThreads&lt;/i&gt; function, and within the &lt;i&gt;extern &quot;C&quot;&lt;/i&gt; block, add the code in the following snippet to your &lt;i&gt;pthreads.cpp&lt;/i&gt; file:
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color:blue;&quot;&gt;struct&lt;/span&gt; thread_args 
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    uint8_t* image_data;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt; start_index;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt; stop_index;
  &lt;/div&gt;
  };

  &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;void&lt;/span&gt;* thread_func(&lt;span style=&quot;color:blue;&quot;&gt;void&lt;/span&gt;* arg) 
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color:blue;&quot;&gt;struct&lt;/span&gt; thread_args* args = (&lt;span style=&quot;color:blue;&quot;&gt;struct&lt;/span&gt; thread_args*)arg;
    &lt;br /&gt;AdjustPixels(args-&amp;gt;image_data, args-&amp;gt;start_index, args-&amp;gt;stop_index);
 
    &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;return&lt;/span&gt; arg;
  &lt;/div&gt;
  }
&lt;/div&gt;

&lt;br /&gt;
The final function that you&#39;re going to create is the &lt;i&gt;AdjustImageUsingThreads&lt;/i&gt; function. For the threading in this function, you&#39;ll create four pthreads because there are four bytes per pixel (RGBA). You can use any number of threads so long as you divide up the chunks so that each grouping keeps that in mind.

&lt;br /&gt;&lt;br /&gt;
At the beginning of this article it was mentioned that WebAssembly pthreads make use of existing browser features. Each pthread will run in a web worker. Something to be aware of is that web workers have overhead and take some time to start up. It&#39;s not usually noticeable if you only have a couple of web workers but the startup time becomes noticeable as the number of threads increase. 

&lt;br /&gt;&lt;br /&gt;
As you&#39;ll see in a moment, when you compile this code, you&#39;ll tell Emscripten how many threads you want. When the WebAssembly module is being instantiated, all of the threads that you asked for are spun up and placed into a thread pool for use when you&#39;re ready for them.

&lt;br /&gt;&lt;br /&gt;
You&#39;ll want to be as precise as possible with how many threads you request because it wastes device resources if some are spun up and never used. Also, depending on how many threads you request, you may notice a short delay before your module is ready to be interacted with.

&lt;br /&gt;&lt;br /&gt;
My recommendation is that you test to see what you feel is the right balance between startup time and processing power.

&lt;br /&gt;&lt;br /&gt;
The full version of the &lt;i&gt;AdjustImageUsingThreads&lt;/i&gt; function will be shown in a moment.

&lt;br /&gt;&lt;br /&gt;
As shown in the following snippet, the &lt;i&gt;AdjustImageUsingThreads&lt;/i&gt; starts off the same as the &lt;i&gt;AdjustImageWithoutUsingThreads&lt;/i&gt; function:
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  EMSCRIPTEN_KEEPALIVE
  &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;void&lt;/span&gt; AdjustImageUsingThreads(uint8_t* image_data, &lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt; image_data_size)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    std::chrono::high_resolution_clock::time_point duration_start = 
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::chrono::high_resolution_clock::now();
    
    &lt;br /&gt;&lt;br /&gt;...
  &lt;/div&gt;
  }
&lt;/div&gt;

&lt;br /&gt;
Next, you&#39;ll declare a few variables:
&lt;ul&gt;
  &lt;li&gt;The first variable is an array of &lt;i&gt;pthread_t&lt;/i&gt; that will hold the thread ids of each thread that&#39;s created.&lt;/li&gt;
  &lt;li&gt;The second variable is an array of &lt;i&gt;thread_args&lt;/i&gt; that will tell each thread function which grouping of indexes to modify.&lt;/li&gt;
  &lt;li&gt;The third variable holds the number of bytes that each thread is to modify.&lt;/li&gt;
&lt;/ul&gt;
  
&lt;br /&gt;
The next step after declaring the variables is to create a loop that will set the values for the &lt;i&gt;thread_args&lt;/i&gt; array at that index. Then the loop will create the thread. At the end of the loop, the next loop&#39;s start index is the index where the current loop stopped.

&lt;br /&gt;&lt;br /&gt;
The following snippet shows the variable declaration and thread creation loop:
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  EMSCRIPTEN_KEEPALIVE
  &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;void&lt;/span&gt; AdjustImageUsingThreads(uint8_t* image_data, &lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt; image_data_size)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    ...

    &lt;br /&gt;&lt;br /&gt;pthread_t thread_ids[4];
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;struct&lt;/span&gt; thread_args args[4];
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt; grouping_size = (image_data_size / 4);
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt; start_index = 0;

    &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// Spin up each thread...&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;for&lt;/span&gt; (&lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt; i = 0; i &amp;lt; 4; i++) 
    &lt;br /&gt;{
    &lt;div class=&quot;divIndent&quot;&gt;
      args[i].image_data = image_data;
      &lt;br /&gt;args[i].start_index = start_index;
      &lt;br /&gt;args[i].stop_index = (start_index + grouping_size);

      &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (pthread_create(&amp;amp;thread_ids[i], NULL, thread_func, &amp;amp;args[i]))
      &lt;br /&gt;{
      &lt;div class=&quot;divIndent&quot;&gt;
        perror(&lt;span style=&quot;color:maroon;&quot;&gt;&quot;Thread create failed&quot;&lt;/span&gt;);
        &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;return&lt;/span&gt;;
      &lt;/div&gt;
      }

      &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// thread_func will stop 1 less than the stop_index value so that&#39;s the&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// next start index&lt;/span&gt;
      &lt;br /&gt;start_index = args[i].stop_index;
    &lt;/div&gt;
    }
    
    &lt;br /&gt;&lt;br /&gt;...
  &lt;/div&gt;
  }
&lt;/div&gt;
  
&lt;br /&gt;
Next, the function will loop again but this time to wait for each of the threads to finish as shown in the following snippet:
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  EMSCRIPTEN_KEEPALIVE
  &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;void&lt;/span&gt; AdjustImageUsingThreads(uint8_t* image_data, &lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt; image_data_size)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    ...
    
    &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;for&lt;/span&gt; (&lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt; j = 0; j &amp;lt; 4; j++)
    &lt;br /&gt;{
    &lt;div class=&quot;divIndent&quot;&gt;
      pthread_join(thread_ids[j], NULL);
    &lt;/div&gt;
    }
    
    &lt;br /&gt;&lt;br /&gt;...
  &lt;/div&gt;
  }
&lt;/div&gt;
  
&lt;br /&gt;
The function finishes off the same as the &lt;i&gt;AdjustImageWithoutUsingThreads&lt;/i&gt; function does by calculating how long it takes the code to execute.

&lt;br /&gt;&lt;br /&gt;
The full code for the &lt;i&gt;AdjustImageUsingThreads&lt;/i&gt; function is shown in the following code snippet. Add the following code after the &lt;i&gt;thread_func&lt;/i&gt; function, and within the &lt;i&gt;extern &quot;C&quot;&lt;/i&gt; code block of your &lt;i&gt;pthreads.cpp&lt;/i&gt; file:
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  EMSCRIPTEN_KEEPALIVE
  &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;void&lt;/span&gt; AdjustImageUsingThreads(uint8_t* image_data, &lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt; image_data_size)
  &lt;br /&gt;{
  &lt;div class=&quot;divIndent&quot;&gt;
    std::chrono::high_resolution_clock::time_point duration_start = 
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::chrono::high_resolution_clock::now();

    &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// There are 4 bytes per pixel so make sure the threads are working on&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// the data in multiples of 4&lt;/span&gt;
    &lt;br /&gt;pthread_t thread_ids[4];
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;struct&lt;/span&gt; thread_args args[4];
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt; grouping_size = (image_data_size / 4);
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt; start_index = 0;

    &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// Spin up each thread...&lt;/span&gt;
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;for&lt;/span&gt; (&lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt; i = 0; i &amp;lt; 4; i++) 
    &lt;br /&gt;{
    &lt;div class=&quot;divIndent&quot;&gt;
      args[i].image_data = image_data;
      &lt;br /&gt;args[i].start_index = start_index;
      &lt;br /&gt;args[i].stop_index = (start_index + grouping_size);

      &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;if&lt;/span&gt; (pthread_create(&amp;amp;thread_ids[i], NULL, thread_func, &amp;amp;args[i]))
      &lt;br /&gt;{
      &lt;div class=&quot;divIndent&quot;&gt;
        perror(&lt;span style=&quot;color:maroon;&quot;&gt;&quot;Thread create failed&quot;&lt;/span&gt;);
        &lt;br /&gt;return;
      &lt;/div&gt;
      }

      &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// thread_func will stop 1 less than the stop_index value so that&#39;s the&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// next start index&lt;/span&gt;
      &lt;br /&gt;start_index = args[i].stop_index;
    &lt;/div&gt;
    }
      
    &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// Wait for each of the threads to finish...&lt;/span&gt; 
    &lt;br /&gt;&lt;span style=&quot;color:blue;&quot;&gt;for&lt;/span&gt; (&lt;span style=&quot;color:blue;&quot;&gt;int&lt;/span&gt; j = 0; j &amp;lt; 4; j++)
    &lt;br /&gt;{
    &lt;div class=&quot;divIndent&quot;&gt;
      pthread_join(thread_ids[j], NULL);
    &lt;/div&gt;
    }    

    &lt;br /&gt;&lt;br /&gt;std::chrono::high_resolution_clock::time_point duration_end = 
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::chrono::high_resolution_clock::now();
    &lt;br /&gt;std::chrono::duration&amp;lt;&lt;span style=&quot;color:blue;&quot;&gt;double&lt;/span&gt; std::milli&amp;gt; duration = 
    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(duration_end - duration_start);

    &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color:darkgreen;&quot;&gt;// Convert the value into a normal double&lt;/span&gt;
    &lt;br /&gt;execution_duration = duration.count();

    &lt;br /&gt;&lt;br /&gt;printf(&lt;span style=&quot;color:maroon;&quot;&gt;&quot;AdjustImageUsingThreads took %f milliseconds to execute.\n&quot;&lt;/span&gt;, duration.count());
  &lt;/div&gt;
  }
&lt;/div&gt;

&lt;br /&gt;
Save the &lt;i&gt;pthreads.cpp&lt;/i&gt; file.

&lt;br /&gt;&lt;br /&gt;
With the C++ file created, your next step is to compile it into a WebAssembly module.

  
&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;Compiling the code into a WebAssembly module&lt;/div&gt;

&lt;br /&gt;
The Emscripten version used for this article was &lt;i&gt;1.39.20&lt;/i&gt;. If you don&#39;t already have Emscripten installed on your machine, you can download it from the following web page by clicking on the green &lt;i&gt;Code&lt;/i&gt; button and then clicking &lt;i&gt;Download ZIP&lt;/i&gt;: &lt;a href=&quot;https://github.com/emscripten-core/emscripten&quot; target=&quot;_blank&quot;&gt;https://github.com/emscripten-core/emscripten&lt;/a&gt;

&lt;br /&gt;&lt;br /&gt;
The installation instructions for Emscripten can be found here: &lt;a href=&quot;https://emscripten.org/docs/getting_started/downloads.html&quot; target=&quot;_blank&quot;&gt;https://emscripten.org/docs/getting_started/downloads.html&lt;/a&gt;

&lt;br /&gt;&lt;br /&gt;
Some of the C++ features used in the code you just wrote, like the &lt;i&gt;uint8_t&lt;/i&gt; data type, require a minimum of &lt;i&gt;C++11&lt;/i&gt;. By default, Emscripten&#39;s front-end compiler uses &lt;i&gt;C++98&lt;/i&gt; but this can be changed by specifying the &lt;i&gt;-std=c++11&lt;/i&gt; command line flag.

&lt;br /&gt;&lt;br /&gt;
Memory growth is slow but you need to allow the memory to grow (&lt;i&gt;-s ALLOW_MEMORY_GROWTH=1&lt;/i&gt; command line flag) because you don&#39;t know what image sizes your users will try to upload. What you can do though is try to pick a large enough initial memory size that seems reasonable and, if the user&#39;s file exceeds that, then let the memory grow. Perhaps display a warning to the user if the file is larger than the initial memory size because you&#39;ll know how many bytes the file has before you ask the module to allocate the memory for it.

&lt;br /&gt;&lt;br /&gt;
To specify an initial amount of memory, as bytes, you&#39;ll use the &lt;i&gt;-s INITIAL_MEMORY&lt;/i&gt; flag. By default, this value is 16 MB (16,777,216 bytes). For this module, you&#39;ll set the initial memory to &lt;i&gt;64 MB (67,108,864 bytes)&lt;/i&gt;.

&lt;br /&gt;&lt;br /&gt;
To enable pthread support you need to specify the &lt;i&gt;-s USE_PTHREADS=1&lt;/i&gt; flag. You also want to use &lt;i&gt;4 pthreads&lt;/i&gt; so you need to tell Emscripten that by using the &lt;i&gt;-s PTHREAD_POOL_SIZE=4&lt;/i&gt; flag. 

&lt;br /&gt;&lt;br /&gt;
There are various levels of optimization that are available. You&#39;ll use the &lt;i&gt;-O3&lt;/i&gt; level (O is not a number, it&#39;s a capital o). 

&lt;br /&gt;&lt;br /&gt;
The last item that you&#39;ll specify is what type of output you want and where you&#39;d like it to be created by using the &lt;i&gt;-o&lt;/i&gt; flag. You&#39;ll have Emscripten create its JavaScript code and the WebAssembly module in your &lt;i&gt;fontend\js&lt;/i&gt; folder.

&lt;br /&gt;&lt;br /&gt;
To compile your &lt;i&gt;pthreads.cpp&lt;/i&gt; file into a WebAssembly module, open a command prompt, navigate to your &lt;i&gt;source&lt;/i&gt; folder, and then run the following command (note that the line wraps here but it should be all one line at the command prompt):
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  emcc pthreads.cpp -std=c++11 -s TOTAL_MEMORY=67108864
  &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-s ALLOW_MEMORY_GROWTH=1 -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=4
  &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-O3 -o ..\frontend\js\emscripten_pthread.js
&lt;/div&gt;

&lt;br /&gt;
You&#39;ll likely see a warning about the use of the &lt;i&gt;ALLOW_MEMORY_GROWTH&lt;/i&gt; flag but there shouldn&#39;t be any errors and you should now have three new files in your &lt;i&gt;frontend\js&lt;/i&gt; folder:
&lt;ul&gt;
  &lt;li&gt;emscripten_pthread.js&lt;/li&gt;
  &lt;li&gt;emscripten_pthread.wasm&lt;/li&gt;
  &lt;li&gt;emscripten_pthread.worker.js&lt;/li&gt;
&lt;/ul&gt;
  
&lt;br /&gt;
Now that your web page and WebAssembly module are created, it&#39;s time to test the web page to see the results.

    
&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;Viewing the results&lt;/div&gt;

&lt;br /&gt;
If you&#39;re using the Python web server extension that you modified earlier, open a command prompt, navigate to your &lt;i&gt;frontend&lt;/i&gt; folder, and then run the following command:
  
&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  python wasm-server.py
&lt;/div&gt;
  
&lt;br /&gt;
Open Firefox 79 or higher and type &lt;i&gt;http://localhost:8080/pthreads.html&lt;/i&gt; into the address box to see your web page: 
  
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmCvLUw3hzTvrltsIddl13kXwhRj3Z447l67hI_bmaM0KD8f489EL4-n6ROh6qjDiWtp-yRBFmit4j1b3RbdTXcmugg3_nu9GR7Rj9GpLUKz8Q62YynS0TgkD9CZsq2Pe3KrR4kLXpets/s1610/7+-+Screen+shot+of+final+product+before+selecting+an+image.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
    &lt;img border=&quot;0&quot; data-original-height=&quot;696&quot; data-original-width=&quot;1610&quot; height=&quot;173&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmCvLUw3hzTvrltsIddl13kXwhRj3Z447l67hI_bmaM0KD8f489EL4-n6ROh6qjDiWtp-yRBFmit4j1b3RbdTXcmugg3_nu9GR7Rj9GpLUKz8Q62YynS0TgkD9CZsq2Pe3KrR4kLXpets/w400-h173/7+-+Screen+shot+of+final+product+before+selecting+an+image.png&quot; width=&quot;400&quot; /&gt;
  &lt;/a&gt;
  &lt;br /&gt;(click to view the image full size)
&lt;/div&gt;
  
&lt;br /&gt;
Click the &lt;i&gt;Upload&lt;/i&gt; button to launch a &lt;i&gt;File Upload&lt;/i&gt; window similar to the following image. Select an image and press the &lt;i&gt;Open&lt;/i&gt; button.
 
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNKPc09osczB9BzIfIua-I_fO2zMMd7_Cag-gZTPgnDQngsh6wLJG0a1vMfjR7dch9YszbWETf2xbpdymz7bzuWs88PeIilgaesVs8qc0O-_J31XWNvk6Hv2H3jRjt-BPvdt_nZKEYu_c/s943/8+-+File+Open+dialog.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
    &lt;img border=&quot;0&quot; data-original-height=&quot;577&quot; data-original-width=&quot;943&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNKPc09osczB9BzIfIua-I_fO2zMMd7_Cag-gZTPgnDQngsh6wLJG0a1vMfjR7dch9YszbWETf2xbpdymz7bzuWs88PeIilgaesVs8qc0O-_J31XWNvk6Hv2H3jRjt-BPvdt_nZKEYu_c/s320/8+-+File+Open+dialog.png&quot; width=&quot;320&quot; /&gt;
  &lt;/a&gt;
&lt;/div&gt;
  
&lt;br /&gt;
As shown in the following image, the web page will display the original image, the modified images, and the execution duration for each method used. 
 
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVI1R_k7tmqdQYr7wUimOeebrS16CeSAbcPGTQwhswGRglTyx4d4aN633rhekn9kmARRNYB5UuNpPdZ5Gpl48OFISGa_FouwPJ3XzU8phTa7yw_2h1BbiDIm3gN3Aaevgxt9XMTYFny7A/s1608/9+-+Screen+shot+of+final+product+with+images+shown.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
    &lt;img border=&quot;0&quot; data-original-height=&quot;696&quot; data-original-width=&quot;1608&quot; height=&quot;174&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVI1R_k7tmqdQYr7wUimOeebrS16CeSAbcPGTQwhswGRglTyx4d4aN633rhekn9kmARRNYB5UuNpPdZ5Gpl48OFISGa_FouwPJ3XzU8phTa7yw_2h1BbiDIm3gN3Aaevgxt9XMTYFny7A/w400-h174/9+-+Screen+shot+of+final+product+with+images+shown.png&quot; width=&quot;400&quot; /&gt;
  &lt;/a&gt;
  &lt;br /&gt;(click to view the image full size)
&lt;/div&gt;
    
&lt;br /&gt;
Based on these results, you can see that the WebAssembly non-threaded version is twice as fast as its JavaScript counterpart. The WebAssembly threaded version is five times faster than the JavaScript version.


&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;Summary&lt;/div&gt;

&lt;br /&gt;
As you learned in this article, as of Firefox 79, it&#39;s now possible to use WebAssembly pthreads so long as you specify the &lt;i&gt;Cross-Origin-Opener-Policy&lt;/i&gt; (COOP) response header with the value &lt;i&gt;same-origin&lt;/i&gt; and the &lt;i&gt;Cross-Origin-Embedder-Policy&lt;/i&gt; (COEP) response header with the value &lt;i&gt;require-corp&lt;/i&gt;.

&lt;br /&gt;&lt;br /&gt;
Because of the COEP response header&#39;s &lt;i&gt;require-corp&lt;/i&gt; value, if you want to include resources from another server that you trust, you need to include the &lt;i&gt;crossorigin&lt;/i&gt; attribute.

&lt;br /&gt;&lt;br /&gt;
Although, at the time of this article&#39;s writing, Chrome and Chromium-based browsers like Edge didn&#39;t require the COOP and COEP response headers in order to enable the SharedArrayBuffer, they will require it in the near future.

&lt;br /&gt;&lt;br /&gt;
WebAssembly will create a web worker for each thread you request. The web workers are created when the module is instantiated and, if you request a lot of threads, the startup time for your module may become noticeable.


&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;font-weight: bold;&quot;&gt;Source Code&lt;/span&gt;
  &lt;br /&gt;&lt;br /&gt;The source code for this article can be found in the following github repository: &lt;a href=&quot;https://github.com/cggallant/blog_post_code/tree/master/2020%20-%20July%20-%20WebAssembly%20threads%20in%20Firefox&quot; target=&quot;_blank&quot;&gt;https://github.com/cggallant/blog_post_code/tree/master/2020%20-%20July%20-%20WebAssembly%20threads%20in%20Firefox&lt;/a&gt;
&lt;/div&gt;

  
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot; style=&quot;text-align:justify;&quot;&gt;
  &lt;span style=&quot;font-weight: bold;&quot;&gt;Additional Material on WebAssembly&lt;/span&gt;
  &lt;br /&gt;&lt;br /&gt;
    
  Like what you read and are interested in learning more about WebAssembly? 
  &lt;ul&gt;
    &lt;li&gt;Check out my book &lt;a href=&quot;https://www.manning.com/books/webassembly-in-action&quot; target=&quot;_blank&quot;&gt;&quot;WebAssembly in Action&quot;&lt;/a&gt;
      &lt;br /&gt;&lt;br /&gt;
      &lt;div class=&quot;divIndent&quot;&gt;

        &lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjihyphenhyphenZY09x-tS5RRfD_ztLXykEYG_3DF_dcqI-OgeUnV2EwVHQOejfYW0xAX_UVzCrJeAu7nO5YCiwg0ti7U3vPrZT9NWbfOYn1yHrHw01I271gLKRYe57ajnG_x07VsubSplJiQdhH3og/s0/New+Book+Cover_200px+tall.jpg&quot; align=&quot;right&quot; style=&quot;margin-left:8px;&quot; height=&quot;190&quot; /&gt;
        
        The book introduces the WebAssembly stack and walks you through the process of writing and running browser-based applications. It also covers dynamic linking multiple modules at runtime, using web workers to prefetch a module, threading, using WebAssembly modules in Node.js, working with the WebAssembly text format, debugging, and more.
      
        &lt;br /&gt;&lt;br /&gt;
        The first chapter is free to read and, if you&#39;d like to buy the book, it&#39;s 40% off with the following code: &lt;b&gt;ggallantbl&lt;/b&gt;
      &lt;/div&gt;
      
      &lt;br /&gt;
    &lt;/li&gt;

    &lt;li&gt;&lt;a href=&quot;https://cggallant.blogspot.com/2020/10/blazor-webassembly-and-dovico-time.html&quot; target=&quot;_blank&quot;&gt;Blazor WebAssembly and the Dovico Time Entry Status app&lt;/a&gt;
      &lt;br /&gt;&lt;br /&gt;
      &lt;div class=&quot;divIndent&quot;&gt;
        
        &lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgalLjxNn_Cvja4YjS5Hzwe1kElYuATuJzY-47Z__gNdhSPBjm-B4TKhLBA3QXWygKBm0u7W6LEh5CVPnn9CkSrtFkY_7bkJnLPRkDou2cdDsnIBs97hhecDcZDcXb7AiKzCPWE_Y53Mzw/w640-h408/1+-+ScreenShotOfTheBlazorApp.png&quot; width=&quot;300&quot; align=&quot;right&quot; style=&quot;margin-left:8px;&quot; /&gt;
        
        As I was digging into WebAssembly from a C# perspective for an article that I was preparing to write, I decided to use some research time that my company gave me to dig into Blazor WebAssembly by rewriting a small Java application that I built in 2011.

        &lt;br /&gt;&lt;br /&gt;
        This article walks you through creating the Dovico Time Entry Status app using Blazor WebAssembly.
      &lt;/div&gt;
  
      &lt;br /&gt;
    &lt;/li&gt;

    &lt;li&gt;&lt;a href=&quot;https://platform.uno/blog/using-webassembly-modules-in-c/&quot; target=&quot;_blank&quot;&gt;Using WebAssembly modules in C#&lt;/a&gt;
      &lt;br /&gt;&lt;br /&gt;
      &lt;div class=&quot;divIndent&quot;&gt;
        
        &lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEii4ClFmF5k5JSjvNoomdgQli9O5Od-A-6WFsMRV9CLuDYAicxqapFf_UDUy1aCBttiLq4vEvT546yigg3-GONhCJNVcevTjwNXH1RgK1iP3zfO5TnNgj8D5Tcwrl87kprd__Bqf_OohVY/s711/z_+WebAssembly+in+C%2523_+image+for+my+blog+post+and+social+media.png&quot; width=&quot;300&quot; align=&quot;right&quot; style=&quot;margin-left:8px;&quot; /&gt;
        
        While there were a lot of exciting things being worked on with the WebAssembly System Interface (WASI) at the time of my book&#39;s writing, unfortunately, it wasn&#39;t until after the book went to production that an early preview of the Wasmtime runtime was announced for .NET Core.

        &lt;br /&gt;&lt;br /&gt;
        I wrote this article to show you how your C# code can load and use a WebAssembly module via the Wasmtime runtime for .NET. The article also covers how to create custom model validation with ASP.NET Core MVC.
      &lt;/div&gt;
      
      &lt;br /&gt;
    &lt;/li&gt;

    &lt;li&gt;&lt;a href=&quot;https://cggallant.blogspot.com/2020/01/the-import-statement-with-emscripten.html&quot; target=&quot;_blank&quot;&gt;Using the import statement with an Emscripten-generated WebAssembly module in Vue.js&lt;/a&gt;
      &lt;br /&gt;&lt;br /&gt;
      &lt;div class=&quot;divIndent&quot;&gt;
        
        &lt;img src=&quot;https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Vue.js_Logo_2.svg/200px-Vue.js_Logo_2.svg.png&quot; width=&quot;50&quot; align=&quot;right&quot; style=&quot;margin-left:8px;&quot; /&gt;
        
        Over the 2019 Christmas break, I helped a developer find a way to import an Emscripten-generated WebAssembly module into Vue.js. This article details the solutions found.
      &lt;/div&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/div&gt;
  
&lt;br /&gt;&lt;br /&gt;
Disclaimer: I was not paid to write this article but I am paid royalties on the sale of the book &lt;a href=&quot;https://www.manning.com/books/webassembly-in-action&quot; target=&quot;_blank&quot;&gt;&quot;WebAssembly in Action&quot;&lt;/a&gt;.

&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/5808692067072333752/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2020/07/webassembly-threads-in-firefox.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/5808692067072333752'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/5808692067072333752'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2020/07/webassembly-threads-in-firefox.html' title='WebAssembly threads in Firefox'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1z1mAtb8zm9J830NMg18LyxnW1CjO1DXeppsYZJGnHfkErj7eGmAWdoMhimAunG7RDBgMEuXiAHOtODhZWUC1ecosBwfomqasAazLPvIcSEa-zPmIw4soLMG1HYUUUGByCO2xZ61pry8/s72-c/_ShareImage.png" height="72" width="72"/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-271826710391453719</id><published>2020-07-16T12:04:00.022-03:00</published><updated>2020-10-10T16:47:57.118-03:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="application/wasm"/><category scheme="http://www.blogger.com/atom/ns#" term="http.server"/><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><category scheme="http://www.blogger.com/atom/ns#" term="SimpleHTTPServer"/><category scheme="http://www.blogger.com/atom/ns#" term="wasm"/><category scheme="http://www.blogger.com/atom/ns#" term="web server"/><category scheme="http://www.blogger.com/atom/ns#" term="WebAssembly"/><title type='text'>Extending Python’s Simple HTTP Server</title><content type='html'>&lt;span style=&quot;display:none;&quot;&gt;This article shows you how to extend Python&#39;s Simple HTTP Server. It&#39;s also a precursor to my next article &quot;WebAssembly threads in Firefox&quot; because that article will need two response headers returned which isn&#39;t possible when using Python&#39;s web server.&lt;/span&gt;

&lt;div style=&quot;color:black;font-family:arial;text-align:justify;&quot;&gt;
  &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEil3KiI03GPoT3qT56mZAJk1D0O_dl14A86U8lrjPzfbc7wn_VnW10BeLY8_R394fkNuP1YZv-AFw9RYlufpCsZoQG7DxBjutZ9c0oDCzKroCPh2Mr7X4imFsLHWkJHdCKK9cF8Ec9T76o/s950/python-logo.png&quot; style=&quot;clear: left; float: left; margin-right: 1em;margin-top:4px;&quot;&gt;
      &lt;img border=&quot;0&quot; data-original-height=&quot;650&quot; data-original-width=&quot;950&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEil3KiI03GPoT3qT56mZAJk1D0O_dl14A86U8lrjPzfbc7wn_VnW10BeLY8_R394fkNuP1YZv-AFw9RYlufpCsZoQG7DxBjutZ9c0oDCzKroCPh2Mr7X4imFsLHWkJHdCKK9cF8Ec9T76o/s320/python-logo.png&quot; width=&quot;160&quot; /&gt;
    &lt;/a&gt;
  &lt;/div&gt;
  
  Over the past few days, I started putting together some notes for an article about an upcoming WebAssembly feature in the Firefox browser. The trick with the feature is that, in order to enable it, the web server needs to return certain headers.

&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;color: black; font-family: &amp;quot;arial&amp;quot;; text-align: justify;&quot;&gt;&lt;/div&gt;&lt;div style=&quot;color: black; font-family: &amp;quot;arial&amp;quot;; text-align: justify;&quot;&gt;&lt;/div&gt;&lt;div style=&quot;color: black; font-family: &amp;quot;arial&amp;quot;; text-align: justify;&quot;&gt;&lt;br /&gt;
Because the article I&#39;m going to write will be a continuation of a topic from my book, &lt;a href=&quot;https://www.manning.com/books/webassembly-in-action&quot; target=&quot;_blank&quot;&gt;&quot;WebAssembly in Action&quot;&lt;/a&gt;, I thought it would be best to continue to use Python as the local web server. 

&lt;br /&gt;&lt;br /&gt;
As it turns out, running Python&#39;s web server from the command line doesn&#39;t give an option to include response headers. Fortunately, it&#39;s not hard to extend the web server which you&#39;ll learn how to do in this article.

&lt;br /&gt;&lt;br /&gt;
As a bonus, there&#39;s another advantage to extending Python&#39;s local web server. WebAssembly files (.wasm) need to be served with the &#39;application/wasm&#39; Media Type but Python didn&#39;t have that value specified in versions older than 3.7.5. By extending the web server, you can include the Media Type without needing to modify any of Python&#39;s files which simplifies getting up and running with WebAssembly.

&lt;br /&gt;&lt;br /&gt;
The Python code that you&#39;ll need to write is slightly different between the 2.x and 3.x versions of Python so the first thing you need to do is determine which version you have on your machine. 



&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;Python&#39;s version&lt;/div&gt;

&lt;br /&gt;
To check which version of Python you have installed, open a console window and run the following command:

&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  python --version
&lt;/div&gt;

&lt;br /&gt;
You should see the version displayed similar to the following image:

&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHRrRJW_nOG-NVC_ur5TytGPr-hm6H5x6I9bIZ68ozKAeN1wb4U-MveghwyZVwIhgmyb1Ipch3f9TdK0cU6xFZ0wpg54wEJoddowb_MdpenWnWc_wqCANIT8URzUZ9_oYnQQ6Ca0lbLGs/s1600/Python+version.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;225&quot; data-original-width=&quot;689&quot; height=&quot;104&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHRrRJW_nOG-NVC_ur5TytGPr-hm6H5x6I9bIZ68ozKAeN1wb4U-MveghwyZVwIhgmyb1Ipch3f9TdK0cU6xFZ0wpg54wEJoddowb_MdpenWnWc_wqCANIT8URzUZ9_oYnQQ6Ca0lbLGs/s320/Python+version.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;
  &lt;br /&gt;(click to view the image full size)
&lt;/div&gt;

&lt;br /&gt;
If you have Python 3.x installed, skip the following section and go to the &lt;span style=&quot;font-weight: bold;&quot;&gt;&quot;Extending Python 3&#39;s web server&quot;&lt;/span&gt; section.



&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;Extending Python 2&#39;s web server&lt;/div&gt;

&lt;br /&gt;
The first thing that you need to do is create a file for the python code and name it &lt;span style=&quot;font-weight: bold;&quot;&gt;wasm-server.py&lt;/span&gt;

&lt;br /&gt;&lt;br /&gt;
Open the file in the IDE of your choice and then add the following import statements:

&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;import&lt;/span&gt; SimpleHTTPServer
  &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;import&lt;/span&gt; SocketServer
&lt;/div&gt;

&lt;br /&gt;
Next, define a subclass of the &lt;span style=&quot;font-style: italic;&quot;&gt;SimpleHTTPServer.SimpleHTTPRequestHandler&lt;/span&gt; and override the &lt;span style=&quot;font-style: italic;&quot;&gt;end_handlers&lt;/span&gt; method. We won&#39;t return any additional headers in this article but this method allows you to return additional response headers like CORS (Cross-Origin Resource Sharing) for example. End the method by calling the base class so that it will run its code too. Add the following code to your &lt;span style=&quot;font-style: italic;&quot;&gt;wasm-server.py&lt;/span&gt; file after the &lt;span style=&quot;font-style: italic;&quot;&gt;import statements&lt;/span&gt;:

&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;class&lt;/span&gt; WasmHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color: blue;&quot;&gt;def&lt;/span&gt; end_headers(&lt;span style=&quot;color: blue;&quot;&gt;self&lt;/span&gt;):
    &lt;div class=&quot;divIndent&quot;&gt;
      &lt;span style=&quot;color: darkgreen;&quot;&gt;# Include additional response headers here. CORS for example:&lt;/span&gt;
      &lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;#self.send_header(&#39;Access-Control-Allow-Origin&#39;, &#39;*&#39;)&lt;/span&gt;
      &lt;br /&gt;SimpleHTTPServer.SimpleHTTPRequestHandler.end_headers(&lt;span style=&quot;color: blue;&quot;&gt;self&lt;/span&gt;)
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;br /&gt;
Insert a couple of line feeds after the &lt;span style=&quot;font-style: italic;&quot;&gt;end_headers&lt;/span&gt; method and then add the following Media Type for WebAssembly files:

&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  WasmHandler.extensions_map[&lt;span style=&quot;color: maroon;&quot;&gt;&#39;.wasm&#39;&lt;/span&gt;] = &lt;span style=&quot;color: maroon;&quot;&gt;&#39;application/wasm&#39;&lt;/span&gt;
&lt;/div&gt;

&lt;br /&gt;
You&#39;re now going to add some code that will start up the web server. Python files can be loaded by other files (modules) or run directly. You&#39;ll be running it directly but, if it were to be loaded by another module, you wouldn&#39;t want to start up the web server in that case because the calling module will likely handle that. To check if the module is being run directly, you check the &lt;span style=&quot;font-style: italic;&quot;&gt;__name__&lt;/span&gt; variable to see if it holds the string &lt;span style=&quot;font-style: italic;&quot;&gt;&quot;__main__&quot;&lt;/span&gt; which is set by the Python interpreter when run directly.

&lt;br /&gt;&lt;br /&gt;
Following the &lt;span style=&quot;font-style: italic;&quot;&gt;WasmHandler.extensions_map&lt;/span&gt; line of code, add a couple of line feeds and then the following code to start up the web server when the module is run directly:

&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;if&lt;/span&gt; __name__ == &lt;span style=&quot;color: maroon;&quot;&gt;&#39;__main__&#39;&lt;/span&gt;:
  &lt;div class=&quot;divIndent&quot;&gt;
    PORT = 8080
    &lt;br /&gt;httpd = SocketServer.TCPServer((&lt;span style=&quot;color: maroon;&quot;&gt;&quot;&quot;&lt;/span&gt;, PORT), WasmHandler)
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;print&lt;/span&gt;(&lt;span style=&quot;color: maroon;&quot;&gt;&quot;Listening on port {}. Press Ctrl+C to stop.&quot;&lt;/span&gt;.format(PORT))
    &lt;br /&gt;httpd.serve_forever()
  &lt;/div&gt;
&lt;/div&gt;

&lt;br /&gt;
Save the &lt;span style=&quot;font-style: italic;&quot;&gt;wasm-server.py&lt;/span&gt; file.

&lt;br /&gt;&lt;br /&gt;
The following section will show the steps needed to extend Python&#39;s web server if you&#39;re using version 3.x. Skip the following section if you completed the previous section &quot;Extending Python 2&#39;s web server&quot;.



&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;Extending Python 3&#39;s web server&lt;/div&gt;

&lt;br /&gt;
The first thing that you need to do is create a file for the python code and name it &lt;span style=&quot;font-weight: bold;&quot;&gt;wasm-server.py&lt;/span&gt;

&lt;br /&gt;&lt;br /&gt;
Open the file in the IDE of your choice and then add the following import statements:

&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;import&lt;/span&gt; sys
  &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;import&lt;/span&gt; socketserver
  &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;from&lt;/span&gt; http.server &lt;span style=&quot;color: blue;&quot;&gt;import&lt;/span&gt; SimpleHTTPRequestHandler
&lt;/div&gt;

&lt;br /&gt;
Next, define a subclass of the &lt;span style=&quot;font-style: italic;&quot;&gt;SimpleHTTPRequestHandler&lt;/span&gt; and override the &lt;span style=&quot;font-style: italic;&quot;&gt;end_handlers&lt;/span&gt; method. We won&#39;t return any additional headers in this article but this method allows you to return additional response headers like CORS (Cross-Origin Resource Sharing) for example. End the method by calling the base class so that it will run its code too. Add the following code to your &lt;span style=&quot;font-style: italic;&quot;&gt;wasm-server.py&lt;/span&gt; file after the &lt;span style=&quot;font-style: italic;&quot;&gt;import statements&lt;/span&gt;:

&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;class&lt;/span&gt; WasmHandler(SimpleHTTPRequestHandler):
  &lt;div class=&quot;divIndent&quot;&gt;
    &lt;span style=&quot;color: blue;&quot;&gt;def&lt;/span&gt; end_headers(&lt;span style=&quot;color: blue;&quot;&gt;self&lt;/span&gt;): 
    &lt;div class=&quot;divIndent&quot;&gt;
        &lt;span style=&quot;color: darkgreen;&quot;&gt;# Include additional response headers here. CORS for example:&lt;/span&gt;
        &lt;br /&gt;&lt;span style=&quot;color: darkgreen;&quot;&gt;#self.send_header(&#39;Access-Control-Allow-Origin&#39;, &#39;*&#39;)&lt;/span&gt;
        &lt;br /&gt;SimpleHTTPRequestHandler.end_headers(&lt;span style=&quot;color: blue;&quot;&gt;self&lt;/span&gt;)
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;br /&gt;
In Python 3.7.5, the WebAssembly Media Type was added but it didn&#39;t exist before that version. Insert a couple of line feeds after the &lt;span style=&quot;font-style: italic;&quot;&gt;end_headers&lt;/span&gt; method and then add the following code that checks to see if the version of Python is less than 3.7.5. If so, include the Media Type for WebAssembly files:

&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;if&lt;/span&gt; sys.version_info &amp;lt; (3, 7, 5):
  &lt;div class=&quot;divIndent&quot;&gt;
    WasmHandler.extensions_map[&lt;span style=&quot;color: maroon;&quot;&gt;&#39;.wasm&#39;&lt;/span&gt;] = &lt;span style=&quot;color: maroon;&quot;&gt;&#39;application/wasm&#39;&lt;/span&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;br /&gt;
You&#39;re now going to add some code that will start up the web server. Python files can be loaded by other files (modules) or run directly. You&#39;ll be running it directly but, if it were to be loaded by another module, you wouldn&#39;t want to start up the web server in that case because the calling module will likely handle that. To check if the module is being run directly, you check the &lt;span style=&quot;font-style: italic;&quot;&gt;__name__&lt;/span&gt; variable to see if it holds the string &lt;span style=&quot;font-style: italic;&quot;&gt;&quot;__main__&quot;&lt;/span&gt; which is set by the Python interpreter when run directly.

&lt;br /&gt;&lt;br /&gt;
Following the &lt;span style=&quot;font-style: italic;&quot;&gt;WasmHandler.extensions_map&lt;/span&gt; line of code, add a couple of line feeds and then the following code to start up the web server when the module is run directly:

&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;color: blue;&quot;&gt;if&lt;/span&gt; __name__ == &lt;span style=&quot;color: maroon;&quot;&gt;&#39;__main__&#39;&lt;/span&gt;:
  &lt;div class=&quot;divIndent&quot;&gt;
    PORT = 8080
    &lt;br /&gt;&lt;span style=&quot;color: blue;&quot;&gt;with&lt;/span&gt; socketserver.TCPServer((&lt;span style=&quot;color: maroon;&quot;&gt;&quot;&quot;&lt;/span&gt;, PORT), WasmHandler) &lt;span style=&quot;color: blue;&quot;&gt;as&lt;/span&gt; httpd:
    &lt;div class=&quot;divIndent&quot;&gt;
        &lt;span style=&quot;color: blue;&quot;&gt;print&lt;/span&gt;(&lt;span style=&quot;color: maroon;&quot;&gt;&quot;Listening on port {}. Press Ctrl+C to stop.&quot;&lt;/span&gt;.format(PORT))
        &lt;br /&gt;httpd.serve_forever()
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;br /&gt;
Save the &lt;span style=&quot;font-style: italic;&quot;&gt;wasm-server.py&lt;/span&gt; file.

&lt;br /&gt;&lt;br /&gt;
Now that you have your wasm-server.py file created, it&#39;s time to test it.



&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;Running the extended web server&lt;/div&gt;

&lt;br /&gt;
You can test your extended web server by opening a console window and executing the following command:

&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  python wasm-server.py
&lt;/div&gt;

&lt;br /&gt;
You should see output similar to the following displayed:

&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
  &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1fxv4rSheCRukiFoxWbRv6IfSVeaRl9BJ5iWfkd2HnMAMK8ieA0rSS-Swp9rkyzU56iKpxnizywiJ7mFbLBw4-z-PGnGX-KV0jDbf46MM6Q1guu5zaydhJ6ZlNPvykdBQWhoE6434Am8/s1600/Python+extended+server+running.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;225&quot; data-original-width=&quot;594&quot; height=&quot;121&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1fxv4rSheCRukiFoxWbRv6IfSVeaRl9BJ5iWfkd2HnMAMK8ieA0rSS-Swp9rkyzU56iKpxnizywiJ7mFbLBw4-z-PGnGX-KV0jDbf46MM6Q1guu5zaydhJ6ZlNPvykdBQWhoE6434Am8/s320/Python+extended+server+running.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;
  &lt;br /&gt;(click to view the image full size)
&lt;/div&gt;

&lt;br /&gt;
If you place an HTML file in that folder called &lt;span style=&quot;font-style: italic;&quot;&gt;test.html&lt;/span&gt;, for example, you could open your browser, type the following into the address bar and Python&#39;s simple HTTP server will serve it: 

&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  http://localhost:8080/test.html
&lt;/div&gt;


&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight: bold;&quot;&gt;Summary&lt;/div&gt;

&lt;br /&gt;
In this article you learned how to extend Python&#39;s Simple HTTP Server so that you can return additional response headers if need be.

&lt;br /&gt;&lt;br /&gt;
You also learned how to include the WebAssembly Media Type, application/wasm, if the version of Python in use is doesn&#39;t include it.

&lt;br /&gt;&lt;br /&gt;
As of version 3.7.5, Python includes the necessary WebAssembly Media Type.


&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  &lt;span style=&quot;font-weight: bold;&quot;&gt;Source Code&lt;/span&gt;
  &lt;br /&gt;&lt;br /&gt;The source code for this article can be found in the following github repository:
  &lt;a href=&quot;https://github.com/cggallant/blog_post_code/tree/master/2020%20-%20July%20-%20Extending%20Python%E2%80%99s%20Simple%20HTTP%20Server&quot; target=&quot;_blank&quot;&gt;https://github.com/cggallant/blog_post_code/tree/master/2020%20-%20July%20-%20Extending%20Python%E2%80%99s%20Simple%20HTTP%20Server&lt;/a&gt;
&lt;/div&gt;

  
&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot; style=&quot;text-align:justify;&quot;&gt;
  &lt;span style=&quot;font-weight: bold;&quot;&gt;Additional Material on WebAssembly&lt;/span&gt;
  &lt;br /&gt;&lt;br /&gt;
    
  Like what you read and are interested in learning more about WebAssembly? 
  &lt;ul&gt;
    &lt;li&gt;Check out my book &lt;a href=&quot;https://www.manning.com/books/webassembly-in-action&quot; target=&quot;_blank&quot;&gt;&quot;WebAssembly in Action&quot;&lt;/a&gt;
      &lt;br /&gt;&lt;br /&gt;
      &lt;div class=&quot;divIndent&quot;&gt;

        &lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjihyphenhyphenZY09x-tS5RRfD_ztLXykEYG_3DF_dcqI-OgeUnV2EwVHQOejfYW0xAX_UVzCrJeAu7nO5YCiwg0ti7U3vPrZT9NWbfOYn1yHrHw01I271gLKRYe57ajnG_x07VsubSplJiQdhH3og/s0/New+Book+Cover_200px+tall.jpg&quot; align=&quot;right&quot; style=&quot;margin-left:8px;&quot; height=&quot;190&quot; /&gt;
        
        The book introduces the WebAssembly stack and walks you through the process of writing and running browser-based applications. It also covers dynamic linking multiple modules at runtime, using web workers to prefetch a module, threading, using WebAssembly modules in Node.js, working with the WebAssembly text format, debugging, and more.
      
        &lt;br /&gt;&lt;br /&gt;
        The first chapter is free to read and, if you&#39;d like to buy the book, it&#39;s 40% off with the following code: &lt;b&gt;ggallantbl&lt;/b&gt;
      &lt;/div&gt;
      
      &lt;br /&gt;
    &lt;/li&gt;

    &lt;li&gt;&lt;a href=&quot;https://cggallant.blogspot.com/2020/10/blazor-webassembly-and-dovico-time.html&quot; target=&quot;_blank&quot;&gt;Blazor WebAssembly and the Dovico Time Entry Status app&lt;/a&gt;
      &lt;br /&gt;&lt;br /&gt;
      &lt;div class=&quot;divIndent&quot;&gt;
        
        &lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgalLjxNn_Cvja4YjS5Hzwe1kElYuATuJzY-47Z__gNdhSPBjm-B4TKhLBA3QXWygKBm0u7W6LEh5CVPnn9CkSrtFkY_7bkJnLPRkDou2cdDsnIBs97hhecDcZDcXb7AiKzCPWE_Y53Mzw/w640-h408/1+-+ScreenShotOfTheBlazorApp.png&quot; width=&quot;300&quot; align=&quot;right&quot; style=&quot;margin-left:8px;&quot; /&gt;
        
        As I was digging into WebAssembly from a C# perspective for an article that I was preparing to write, I decided to use some research time that my company gave me to dig into Blazor WebAssembly by rewriting a small Java application that I built in 2011.

        &lt;br /&gt;&lt;br /&gt;
        This article walks you through creating the Dovico Time Entry Status app using Blazor WebAssembly.
      &lt;/div&gt;
  
      &lt;br /&gt;
    &lt;/li&gt;

    &lt;li&gt;&lt;a href=&quot;https://platform.uno/blog/using-webassembly-modules-in-c/&quot; target=&quot;_blank&quot;&gt;Using WebAssembly modules in C#&lt;/a&gt;
      &lt;br /&gt;&lt;br /&gt;
      &lt;div class=&quot;divIndent&quot;&gt;
        
        &lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEii4ClFmF5k5JSjvNoomdgQli9O5Od-A-6WFsMRV9CLuDYAicxqapFf_UDUy1aCBttiLq4vEvT546yigg3-GONhCJNVcevTjwNXH1RgK1iP3zfO5TnNgj8D5Tcwrl87kprd__Bqf_OohVY/s711/z_+WebAssembly+in+C%2523_+image+for+my+blog+post+and+social+media.png&quot; width=&quot;300&quot; align=&quot;right&quot; style=&quot;margin-left:8px;&quot; /&gt;
        
        While there were a lot of exciting things being worked on with the WebAssembly System Interface (WASI) at the time of my book&#39;s writing, unfortunately, it wasn&#39;t until after the book went to production that an early preview of the Wasmtime runtime was announced for .NET Core.

        &lt;br /&gt;&lt;br /&gt;
        I wrote this article to show you how your C# code can load and use a WebAssembly module via the Wasmtime runtime for .NET. The article also covers how to create custom model validation with ASP.NET Core MVC.
      &lt;/div&gt;
      
      &lt;br /&gt;
    &lt;/li&gt;

    &lt;li&gt;&lt;a href=&quot;https://cggallant.blogspot.com/2020/07/webassembly-threads-in-firefox.html&quot; target=&quot;_blank&quot;&gt;WebAssembly threads in Firefox&lt;/a&gt;
      &lt;br /&gt;&lt;br /&gt;
      &lt;div class=&quot;divIndent&quot;&gt;
        
        &lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3Us0sM5ACYfCQInD0DVx-0uIMi6dYN8jDvV4g0F_zXfrJDo0WT8OJx7-2qnpMS_m-qeQ7VAFEKYTC4zWQaviYueoz3kXhIZVKeKnZqHUCY_IjAMdUEvIeQ_VC-C6kR46Am3Q8PvVfMMc/w400-h174/9+-+Screen+shot+of+final+product+with+images+shown.png&quot; width=&quot;300&quot; align=&quot;right&quot; style=&quot;margin-left:8px;&quot; /&gt; 
        
        My book shows you how to use WebAssembly threads but, at the time of its writing, they were only available in Firefox behind a flag. They&#39;re no longer behind a flag but Firefox has added a requirement: To enable the SharedArrayBuffer, you need to include two response headers.

        &lt;br /&gt;&lt;br /&gt;
        Although the headers are only required by Firefox desktop at the time of this article&#39;s writing, this will soon change as Chrome for Android will require the headers when version 88 is released in January 2021. Chrome desktop is expected to require the headers by March 2021.

        &lt;br /&gt;&lt;br /&gt;
        This article walks you through returning the response headers and using WebAssembly threads to convert a user-supplied image to greyscale.
      &lt;/div&gt;
      
      &lt;br /&gt;
    &lt;/li&gt;

    &lt;li&gt;&lt;a href=&quot;https://cggallant.blogspot.com/2020/01/the-import-statement-with-emscripten.html&quot; target=&quot;_blank&quot;&gt;Using the import statement with an Emscripten-generated WebAssembly module in Vue.js&lt;/a&gt;
      &lt;br /&gt;&lt;br /&gt;
      &lt;div class=&quot;divIndent&quot;&gt;
        
        &lt;img src=&quot;https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Vue.js_Logo_2.svg/200px-Vue.js_Logo_2.svg.png&quot; width=&quot;50&quot; align=&quot;right&quot; style=&quot;margin-left:8px;&quot; /&gt;
        
        Over the 2019 Christmas break, I helped a developer find a way to import an Emscripten-generated WebAssembly module into Vue.js. This article details the solutions found.
      &lt;/div&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/div&gt;
  

&lt;br /&gt;&lt;br /&gt;
Disclaimer: I was not paid to write this article but I am paid royalties on the sale of the book &lt;a href=&quot;https://www.manning.com/books/webassembly-in-action&quot; target=&quot;_blank&quot;&gt;&quot;WebAssembly in Action&quot;&lt;/a&gt; which I mentioned in this article.

&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/271826710391453719/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2020/07/extending-pythons-simple-http-server.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/271826710391453719'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/271826710391453719'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2020/07/extending-pythons-simple-http-server.html' title='Extending Python’s Simple HTTP Server'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEil3KiI03GPoT3qT56mZAJk1D0O_dl14A86U8lrjPzfbc7wn_VnW10BeLY8_R394fkNuP1YZv-AFw9RYlufpCsZoQG7DxBjutZ9c0oDCzKroCPh2Mr7X4imFsLHWkJHdCKK9cF8Ec9T76o/s72-c/python-logo.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5258244231997226159.post-6279176644163593450</id><published>2020-03-02T09:20:00.001-04:00</published><updated>2020-03-02T09:20:50.003-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ConFoo"/><category scheme="http://www.blogger.com/atom/ns#" term="Firefox"/><category scheme="http://www.blogger.com/atom/ns#" term="Google Earth"/><category scheme="http://www.blogger.com/atom/ns#" term="Manning Publications"/><category scheme="http://www.blogger.com/atom/ns#" term="Montreal"/><category scheme="http://www.blogger.com/atom/ns#" term="Uno Platform"/><category scheme="http://www.blogger.com/atom/ns#" term="UnoConf"/><category scheme="http://www.blogger.com/atom/ns#" term="WebAssembly"/><title type='text'>Presenting a WebAssembly Overview at ConFoo.ca</title><content type='html'>&lt;div style=&quot;color:black;font-family:arial;text-align:justify;&quot;&gt;

This was my first time attending a &lt;a href=&quot;https://confoo.ca/en/yul2020&quot; target=&quot;_blank&quot;&gt;ConFoo.ca Developer conference&lt;/a&gt;. I also had the pleasure of being one of the speakers at this year&#39;s event so I thought I&#39;d write about my experience.

&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkJaDl3eTtaL5zn08lFKVrOgHyaeihy8SqUZgWd-HP6MvCOznvjwGaCpjfDG9f-hV_NmBc_Dy3zopmkEayIqfnSrVzx00cVFsqKgoyeb8pQ3dJ5I8WezSBwAAnFnLrAFSgQtoWyi4X5-I/s1600/1.+speaker+badge.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkJaDl3eTtaL5zn08lFKVrOgHyaeihy8SqUZgWd-HP6MvCOznvjwGaCpjfDG9f-hV_NmBc_Dy3zopmkEayIqfnSrVzx00cVFsqKgoyeb8pQ3dJ5I8WezSBwAAnFnLrAFSgQtoWyi4X5-I/s320/1.+speaker+badge.jpg&quot; width=&quot;320&quot; height=&quot;224&quot; data-original-width=&quot;513&quot; data-original-height=&quot;359&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;br /&gt;
ConFoo.ca Montreal celebrated its 18th anniversary this year with nearly 840 attendees! The following are some photos that I took while people were still arriving for the first day&#39;s keynote:

&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
 &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitQHWv1PFmwW9xDylRxWzDF72lr-tvmAefAN6nQs4u4eQ2Lr9jlrInYagyMJg7-L2c2ayPtHuNL4BC6EH9u5MvpM4A4X7ODB8LGx2ksLqbuVUYN-x-3_JzBuOQZSwfzK-IIAgwWO36wiI/s1600/2.+key+note+crowd.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitQHWv1PFmwW9xDylRxWzDF72lr-tvmAefAN6nQs4u4eQ2Lr9jlrInYagyMJg7-L2c2ayPtHuNL4BC6EH9u5MvpM4A4X7ODB8LGx2ksLqbuVUYN-x-3_JzBuOQZSwfzK-IIAgwWO36wiI/s400/2.+key+note+crowd.jpg&quot; width=&quot;400&quot; height=&quot;100&quot; data-original-width=&quot;1600&quot; data-original-height=&quot;399&quot; /&gt;&lt;/a&gt;
 &lt;br /&gt;(click to view the image full size)
&lt;/div&gt;

&lt;br /&gt;
The conference brought speakers from around the world who presented 156 talks over three days! I was honored to be included among them by giving a WebAssembly Overview presentation.

&lt;br /&gt;&lt;br /&gt;
I&#39;ve written a book on &lt;a href=&quot;https://www.manning.com/books/webassembly-in-action&quot; target=&quot;_blank&quot;&gt;WebAssembly&lt;/a&gt; so I know the subject matter quite well. I&#39;ve also given public presentations a number of times at work and at local user groups but those audiences are smaller. This is a big conference and I was given one of the bigger rooms so I was a little nervous.

&lt;br /&gt;&lt;br /&gt;
While I was traveling to the conference, and while I was there, a couple WebAssembly announcements were made about Firefox and Google Earth.



&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight:bold;&quot;&gt;WebAssembly Announcements&lt;/div&gt;

&lt;br /&gt;
The first announcement was from Mozilla. They&#39;ve been working on porting their C and C++ code to Rust but there&#39;s a lot of code in Firefox so this will take time.

&lt;br /&gt;&lt;br /&gt;
What they decided to do is leverage WebAssembly within Firefox itself by using RLBox. They started by sandboxing their font library and plan to sandbox other components in the future. For more details, you can visit the following site: &lt;a href=&quot;https://www.zdnet.com/article/firefox-for-mac-and-linux-to-get-a-new-security-sandbox-system/&quot; target=&quot;_blank&quot;&gt;https://www.zdnet.com/article/firefox-for-mac-and-linux-to-get-a-new-security-sandbox-system/&lt;/a&gt;

&lt;br /&gt;&lt;br /&gt;
The announcement from Google Earth was that their WebAssembly version has come out of beta for the desktop version of Firefox, Edge, and Opera. More details on this announcement can be found here: &lt;a href=&quot;https://9to5google.com/2020/02/26/google-earth-firefox-edge/&quot; target=&quot;_blank&quot;&gt;https://9to5google.com/2020/02/26/google-earth-firefox-edge/&lt;/a&gt;

&lt;br /&gt;&lt;br /&gt;
I updated my presentation with the latest WebAssembly news and spent every spare moment I could find going over my notes and slides. I even skipped lunch on the final day of the conference to give my presentation one more run though. 



&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight:bold;&quot;&gt;My turn&lt;/div&gt;

&lt;br /&gt;
It was convenient being the first presenter in the room after lunch because it gave me time to set up my computer and adjust my slides.

&lt;br /&gt;&lt;br /&gt;
The projector was pointing a bit too high and the titles on my slides were being cut off at the top so I had to bump the titles down on every slide. In hindsight, I should have just nudged the projector forward a tiny bit but my nerves got the best of me and I didn&#39;t see the simpler option.

&lt;br /&gt;&lt;br /&gt;
There are a few things that I would change with my presentation but, overall, it went well. There were a decent number of attendees and some good questions afterwards.

&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEik6_IoAbSO_74-EmibbYYe3L4P9l4kJiZc22RDmrgc6QSkY3psC7Qc4GNfjP-eI3sybzbB-6Y9Iix_9El_Ag_vNr1tYvS4rjMjAMm5PYSnsaP1c-6IpwzVvz07u99n1XyinWbxVnLp3pY/s1600/3.+Me+speaking_sm.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEik6_IoAbSO_74-EmibbYYe3L4P9l4kJiZc22RDmrgc6QSkY3psC7Qc4GNfjP-eI3sybzbB-6Y9Iix_9El_Ag_vNr1tYvS4rjMjAMm5PYSnsaP1c-6IpwzVvz07u99n1XyinWbxVnLp3pY/s400/3.+Me+speaking_sm.jpg&quot; width=&quot;400&quot; height=&quot;400&quot; data-original-width=&quot;824&quot; data-original-height=&quot;824&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;br /&gt;
I had been in contact with members of &lt;a href=&quot;https://platform.uno/&quot; target=&quot;_blank&quot;&gt;The Uno Platform&lt;/a&gt; for a while now. When I found out that I was going to be a speaker at ConFoo in Montreal, we arranged a meeting to say hello because their head office is in Montreal too.



&lt;br /&gt;&lt;br /&gt;
&lt;div style=&quot;font-weight:bold;&quot;&gt;The Uno Platform meeting&lt;/div&gt;

&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirqy0veakAwdDT3qwxr0e4h3xIz61Q40l99urH-T2voXPQRTRrNoGLNTGxDxaMpWX57iSI27Y_w4NEcya-W4ESErr9LSfN4JsgDd-0cznQzFIUwJ_FvrJSroyALaAsTR7vTCo9MZuRfeo/s1600/4.+uno+logo.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirqy0veakAwdDT3qwxr0e4h3xIz61Q40l99urH-T2voXPQRTRrNoGLNTGxDxaMpWX57iSI27Y_w4NEcya-W4ESErr9LSfN4JsgDd-0cznQzFIUwJ_FvrJSroyALaAsTR7vTCo9MZuRfeo/s200/4.+uno+logo.jpg&quot; width=&quot;200&quot; height=&quot;82&quot; data-original-width=&quot;348&quot; data-original-height=&quot;143&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;br /&gt;
At the end of the first day&#39;s sessions, I rushed down the road to The Uno Platform&#39;s head office where I had a chance to sit down and chat with their CEO &lt;a href=&quot;https://twitter.com/francoistanguay&quot; target=&quot;_blank&quot;&gt;Francois Tanguay&lt;/a&gt; and CTO &lt;a href=&quot;https://twitter.com/jlaban&quot; target=&quot;_blank&quot;&gt;Jerome Laban&lt;/a&gt; where we talked about...you guessed it...WebAssembly! 

&lt;br /&gt;&lt;br /&gt;
They&#39;re very nice guys who are very passionate about C#, XMAL, and WebAssembly. If you want to do cross platform C# and XAML that targets iOS, Android, desktop, and the web, I recommend checking out &lt;a href=&quot;https://platform.uno/&quot; target=&quot;_blank&quot;&gt;The Uno Platform&lt;/a&gt;.

&lt;br /&gt;&lt;br /&gt;
They&#39;re having a conference &lt;span style=&quot;font-weight:bold;&quot;&gt;August 26-27, 2020&lt;/span&gt; in Montreal called &lt;a href=&quot;https://unoconf.com/&quot; target=&quot;_blank&quot;&gt;UnoConf&lt;/a&gt;. I copied the following from their website:

&lt;br /&gt;&lt;br /&gt;
&lt;div class=&quot;divExample&quot;&gt;
  UnoConf is a gathering point for the complete Uno community – engineering team, influencers, code contributors as well as those wishing to learn more about the Platform.

  &lt;br /&gt;&lt;br /&gt;
  UnoConf 2020 brings a full day conference followed by a full day hands on workshop lead by Uno Platform core team. By August WinUI 3.0 will be in market and you&#39;ll get a front seat in learning how to build cross-platform solutions and how to migrate your existing applications with it.
&lt;/div&gt;



&lt;br /&gt;
&lt;div style=&quot;font-weight:bold;&quot;&gt;Summary&lt;/div&gt;

&lt;br /&gt;
My visit to Montreal went very quick. It feels like it was just yesterday that I was getting on a plane for Montreal and now I&#39;m writing about my experience on a plane heading home.

&lt;br /&gt;&lt;br /&gt;
I want to send out a special thank you to &lt;a href=&quot;https://www.manning.com/&quot; target=&quot;_blank&quot;&gt;Manning Publications&lt;/a&gt; for providing some free e-books for me to give way as well as a special discount code.

&lt;br /&gt;&lt;br /&gt;
I also want to thank &lt;a href=&quot;https://twitter.com/ylarrivee&quot; target=&quot;_blank&quot;&gt;Yann Larrivée&lt;/a&gt; for giving me this opportunity to speak. He worked really hard and did an amazing job to put on a very professional conference.

&lt;br /&gt;&lt;br /&gt;
ConFoo brought in speakers from around the world who are indeed world-class. If you can find a way to get to Montreal next &lt;span style=&quot;font-weight:bold;&quot;&gt;February 24-26, 2021&lt;/span&gt;, I highly recommend attending this conference.

&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cggallant.blogspot.com/feeds/6279176644163593450/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cggallant.blogspot.com/2020/03/presenting-webassembly-overview-at.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/6279176644163593450'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5258244231997226159/posts/default/6279176644163593450'/><link rel='alternate' type='text/html' href='http://cggallant.blogspot.com/2020/03/presenting-webassembly-overview-at.html' title='Presenting a WebAssembly Overview at ConFoo.ca'/><author><name>C. Gerard Gallant</name><uri>http://www.blogger.com/profile/17018683873382556126</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0VA7NpHoAp6cuen-NAQHac26tAhIQjGMcWp_gcyaTyxZqNFE-W4Q_2EvnWT-qLo0A0ttAOdHUDPnFzTOIJUJ9CUFr6ZO-Sc0-govKKF3lOnos36MDU7bGEN4NBZSxTu6rSXbJYDJ7keZAx_dJ-n9k6BSmn74O_OHFLAgJsSMvgNnM/s220/Gerard_Nov8_2022_1000x1000.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkJaDl3eTtaL5zn08lFKVrOgHyaeihy8SqUZgWd-HP6MvCOznvjwGaCpjfDG9f-hV_NmBc_Dy3zopmkEayIqfnSrVzx00cVFsqKgoyeb8pQ3dJ5I8WezSBwAAnFnLrAFSgQtoWyi4X5-I/s72-c/1.+speaker+badge.jpg" height="72" width="72"/><thr:total>0</thr:total></entry></feed>