Pete LePageThoughts on web development, life, and photography.2021-08-09T00:00:00Zhttps://petelepage.com/Pete LePagepete.lepage@pobox.comRaspberry Pi Headless Setup2021-08-09T00:00:00Zhttps://petelepage.com/blog/2021/08/rpi-setup/<p>I recently had to create a new Raspberry Pi image, and thankfully you can now
do it without the need to ever plug the Pi into a keyboard or monitor!</p>
<h2 id="create-%26-configure-the-sd-card">Create & configure the SD card <a class="direct-link" href="https://petelepage.com/blog/2021/08/rpi-setup/#create-%26-configure-the-sd-card">#</a></h2>
<ol>
<li>Use the <a href="https://www.raspberrypi.org/documentation/installation/installing-images/README.md">Raspberry Pi Imager</a> to create a <em>Raspberry Pi OS Lite</em>
SD card.</li>
<li>Mount the SD card.</li>
<li>Enable SSH by creating an empty file named <code>ssh</code> in the <em>boot</em> partition
of the SD card. (<a href="https://www.raspberrypi.org/documentation/remote-access/ssh/README.md">source</a>)</li>
<li>Configure the wireless network by dropping the <code>wpa_supplicant.conf</code> file
into the boot partition of the SD card. (<a href="https://www.raspberrypi.org/documentation/configuration/wireless/headless.md">source</a>)</li>
<li>Unmount SD card from computer, and boot Raspberry Pi.</li>
</ol>
<h2 id="initial-configuration">Initial Configuration <a class="direct-link" href="https://petelepage.com/blog/2021/08/rpi-setup/#initial-configuration">#</a></h2>
<ol>
<li>SSH into <code>raspberrypi</code>, username: <em>pi</em> password: <em>raspberry</em></li>
<li>Run <code>sudo raspi-config</code>
<ol>
<li>Run <em>Update</em> to update to the latest <code>raspi-config</code> tool.</li>
<li>Set <em>System Options</em>, including password, hostname, and auto-login.</li>
<li>Set <em>Display Options</em>, including screen blanking.</li>
<li>Set <em>Performance Options</em>, including Fan, and GPU Memory (16).</li>
<li>Set <em>Localization Options</em>.</li>
<li>Set <em>Advanced Options</em>, including Expand File System.</li>
<li>Exit & reboot.</li>
</ol>
</li>
<li>SSH into Raspberry Pi using new hostname & password.</li>
</ol>
<h3 id="disable-hdmi-cec-(optional)">Disable HDMI CEC (optional) <a class="direct-link" href="https://petelepage.com/blog/2021/08/rpi-setup/#disable-hdmi-cec-(optional)">#</a></h3>
<p>I don’t want my Pi to turn the TV on when it boots, so I disable the HDMI CEC
functionality.</p>
<ol>
<li>
<p>Disable CEC commands via HDMI. (<a href="https://www.raspberrypi.org/documentation/configuration/config-txt/video.md">source</a>)</p>
<p><code>echo "hdmi_ignore_cec=1" | sudo tee -a /boot/config.txt</code></p>
</li>
<li>
<p>Disable CEC active source message being sent during bootup.</p>
<p><code>echo "hdmi_ignore_cec_init=1" | sudo tee -a /boot/config.txt</code></p>
</li>
</ol>
<h3 id="edit-dotfiles">Edit dotfiles <a class="direct-link" href="https://petelepage.com/blog/2021/08/rpi-setup/#edit-dotfiles">#</a></h3>
<ol>
<li>Create <a href="https://gist.github.com/petele/000830e3ba58b47b2b487ac9566867b3#file-bash_prompt"><code>.bash_prompt</code></a></li>
<li>Add <code>source .bash_prompt</code> to bottom of <code>.bashrc</code></li>
</ol>
<h2 id="harden-security-on-system">Harden security on system <a class="direct-link" href="https://petelepage.com/blog/2021/08/rpi-setup/#harden-security-on-system">#</a></h2>
<ol>
<li>Follow steps at <a href="https://www.raspberrypi.org/documentation/configuration/security.md">https://www.raspberrypi.org/documentation/configuration/security.md</a></li>
</ol>
<p><strong>Note:</strong> If you switch username, you’ll need to edit the dotfiles again.</p>
<h3 id="enable-ssh-access-via-key">Enable SSH access via key <a class="direct-link" href="https://petelepage.com/blog/2021/08/rpi-setup/#enable-ssh-access-via-key">#</a></h3>
<ol>
<li>
<p>If you haven’t already, create an SSH key on the remote computer with:</p>
<p><code>ssh-keygen</code></p>
</li>
<li>
<p>From the remote computer, copy key to account on the Pi:</p>
<p><code>ssh-copy-id pi@HOSTNAME</code></p>
</li>
<li>
<p>SSH into the Pi and disable password authentication:</p>
<p><code>echo "PasswordAuthentication no" | sudo tee -a /etc/ssh/sshd_config</code></p>
</li>
</ol>
<h2 id="update-and-install-required-software">Update and install required software <a class="direct-link" href="https://petelepage.com/blog/2021/08/rpi-setup/#update-and-install-required-software">#</a></h2>
<h3 id="update-existing-software">Update existing software <a class="direct-link" href="https://petelepage.com/blog/2021/08/rpi-setup/#update-existing-software">#</a></h3>
<ol>
<li><code>sudo apt -y update && sudo apt -y full-upgrade</code></li>
</ol>
<h3 id="install-additional-packages">Install additional packages <a class="direct-link" href="https://petelepage.com/blog/2021/08/rpi-setup/#install-additional-packages">#</a></h3>
<ol>
<li><code>sudo apt -y install git git-core</code></li>
<li><code>sudo apt -y install lynx netatalk libssl-dev sshfs wiringpi</code></li>
<li><code>sudo apt -y install alsa-utils mpg321 mplayer</code></li>
<li><code>sudo apt -y install bluetooth bluez libbluetooth-dev libudev-dev</code></li>
<li><code>sudo apt -y install libavahi-compat-libdnssd-dev libcap2-bin libtool-bin</code></li>
<li>(optional) <code>sudo apt -y install python-setuptools python-dev python-rpi.gpio</code></li>
<li>(optional) <code>sudo apt -y install android-tools-adb</code></li>
</ol>
<h3 id="install-node-via-nvm">Install Node via NVM <a class="direct-link" href="https://petelepage.com/blog/2021/08/rpi-setup/#install-node-via-nvm">#</a></h3>
<ol>
<li>
<p>Install <a href="https://github.com/creationix/nvm"><code>nvm</code></a></p>
</li>
<li>
<p>Add NVM to path:</p>
<p><code>source ./.bashrc</code></p>
</li>
<li>
<p>Install a current version of Node:</p>
<p><code>nvm install 14</code></p>
</li>
<li>
<p>(optional) Install <em>forever</em>:</p>
<p><code>npm install -g forever</code></p>
</li>
</ol>
How to serve a localized web app manifest for PWAs2021-05-26T00:00:00Zhttps://petelepage.com/blog/2021/05/how-to-serve-a-localized-web-app-manifest-for-pwas/<p class="lead">Do you want to serve a localized experience for your Progressive Web App (PWA)
depending on the users language?</p>
<p>Unfortunately today, there’s no built in mechanism for localizing your web
app manifest. There’s some work being done in the spec, but it hasn’t landed
in any browser yet. <strong>But, it’s possible to do with a little server side code.</strong></p>
<p>First, you’ll need to create a separate manifest file, localized for each
language you want to serve. For example, if I wanted to offer
<a href="https://squoosh.app/">Squoosh</a> in English and French, I’d create two separate
manifest files. That part is easy enough, but now I need to tell the PWA about
the localized manifest file.</p>
<p>For that, I recommend using the <code>accept-language</code> HTTP header on the server
to decide which manifest file to send. This way, the browser will always ask
for the <em>same filename</em>, but will get different content depending on the
user’s preferred language.</p>
<p>To do this with <a href="https://firebase.google.com/docs/hosting">Firebase hosting</a>,
create a <code>localized-files</code> directory, and drop the localized manifest files
into the appropriate language folders. Then add an
<a href="https://firebase.google.com/docs/hosting/i18n-rewrites"><code>i18n</code> property to the <code>firebase.json</code> file</a>, and that’s it.</p>
<p>The server will serve the appropriate file to the user based on their
preferred language. English gets manifest from the English directory, and
French gets the manifest from the French directory.</p>
<p><strong>The configuration will vary for different servers, but the concept remains the same.</strong></p>
<p>If you wanted to take this a step further, provide a language picker within
your app that sets a cookie with the user’s selection. Then serve the
localized manifest with the cookie taking priority over the accept-language
HTTP header.</p>
<p>One of advantages of this implementation is that if the user changes their
preferred language later, the manifest remains at the same path. And in many
cases, the PWA will be automatically updated based on the preferred language.</p>
<p>To learn more about how Chrome handles changes to the manifest, and what
properties will trigger an update, check out How Chrome handles updates to
the web app manifest.</p>
Measure & understand how installed PWA users differ from browser tab users2020-08-19T00:00:00Zhttps://petelepage.com/blog/2020/08/measure-understand-how-installed-pwa-users-differ-from-browser-tab-users/<p>Are PWA users more engaged than browser-tab users? Do they convert more?
Spend more time on site? How can you tell the difference between users who
opened your PWA up via the launcher (standalone/installed users) vs users in
a browser tab? Great question! The easiest way to do it is by using a
dimension to track standalone vs browser.</p>
<p>I’m working on a more complete version of this for <web.dev> with a lot more
detail, how to measure the effectiveness of your install promotions, how
to do it with other analytics providers, etc, but I wanted to get this out
now.</p>
<ol>
<li>In Google Analytics, setup a custom dimension, I called mine <code>DISPLAY_MODE</code></li>
<li>In your page, before you fire the <code>pageview</code> event, set the dimension using
the code below.</li>
</ol>
<pre class="language-js"><code class="language-js"><span class="token keyword">let</span> displayMode <span class="token operator">=</span> <span class="token string">'browser'</span><span class="token punctuation">;</span><br /> <span class="token keyword">const</span> mqStandAlone <span class="token operator">=</span> <span class="token string">'(display-mode: standalone)'</span><span class="token punctuation">;</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>navigator<span class="token punctuation">.</span>standalone <span class="token operator">||</span> window<span class="token punctuation">.</span><span class="token function">matchMedia</span><span class="token punctuation">(</span>mqStandAlone<span class="token punctuation">)</span><span class="token punctuation">.</span>matches<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> displayMode <span class="token operator">=</span> <span class="token string">'standalone'</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token function">ga</span><span class="token punctuation">(</span><span class="token string">'set'</span><span class="token punctuation">,</span> <span class="token string">'dimension1'</span><span class="token punctuation">,</span> displayMode<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Now, comparing <code>DISPLAY_MODE</code> in Google Analytics you can see how <code>standalone</code>
users differ from <code>browser</code> users. I’ve been testing this on
<a href="https://squoosh.app/">Squoosh</a> lately, and it’s interesting to see the
differences. Users in standalone compress more images, come back to Squoosh
more often, and spend more time in Squoosh than those just in a browser tab.</p>
Testing QuotaExceededError by simulating a small disk2020-08-19T00:00:00Zhttps://petelepage.com/blog/2020/07/testing-quotaexceedederror/<p>An unhandled <code>QuotaExceededError</code> when writing to IndexedDB or the Cache
Storage API can cause user data loss. That’s why it’s <strong>critical</strong> to
<a href="https://web.dev/storage-for-the-web/#over-quota">handle them properly</a>. But,
because modern computers typically have large hard drives, exceeding your
quota can be hard to test.</p>
<p>In Chrome, one way to simulate a small drive so that you can properly test how
your code behaves when it hits a <code>QuotaExceededError</code> is to put the user
profile directory on a small RAM disk using the <code>--user-data-dir</code> flag. Since
Chrome bases its storage quota on the size of the disk where the user profile
is, the small RAM disk will limit the storage quota significantly.</p>
<p>To create a 500M RAM disk on macOS, then start Chrome, I used:</p>
<pre class="language-bash"><code class="language-bash">diskutil erasevolume HFS+ ramDisk <span class="token variable"><span class="token variable">$(</span>hdiutil attach -nomount ram://980000<span class="token variable">)</span></span><br /><span class="token function">open</span> -a <span class="token string">"Google Chrome Canary"</span> --args --user-data-dir<span class="token operator">=</span>/Volumes/ramDisk</code></pre>
<p>I know you can do this on Windows and Linux as well, I’m just not sure the
command line required to create a RAM disk for each. Using a RAM disk also
makes it super easy to blow away the profile directory and start fresh,
without any service workers or anything like that.</p>
<p>Want to learn more about web storage, check out my article
<a href="https://web.dev/storage-for-the-web/">Storage for the web</a> and
<a href="https://web.dev/persistent-storage/">Persistent storage</a> on
<a href="https://web.dev/">web.dev</a>.</p>
GitHub SSH and 2-factor auth2020-06-24T00:00:00Zhttps://petelepage.com/blog/2020/06/github-ssh-and-2-factor-auth/<p>I hate that using SSH & 2-factor auth means I have to auth even for for a
git pull. But there’s an easier way! Change the remote used for fetch to
the HTTPS url.</p>
<p>First, verify that both push/fetch are using SSH by running <code>git remote -v</code>
it should output something like:</p>
<pre class="language-text"><code class="language-text">origin git@github.com:petele/squoosh.git (fetch)<br />origin git@github.com:petele/squoosh.git (push)</code></pre>
<p>Then use <code>git remote set-url origin <https url></code> to set the remote URL for
fetch to the HTTPS url. Your repo will now push via SSH and pull via HTTPS,
<strong>and</strong> it won’t require the extra 2-factor auth to pull.</p>
<p>Now, after running <code>git remote -v</code> I get…</p>
<pre class="language-text"><code class="language-text">origin https://github.com/petele/squoosh.git (fetch)<br />origin git@github.com:petele/squoosh.git (push)</code></pre>
Books I'm reading2020-04-27T00:00:00Zhttps://petelepage.com/blog/2020/04/books-im-reading/<p>Reading is critical to my mental health. Ever since I was a kid, I read, at
least a little bit before I fall asleep. In fact, it’s almost a trained
behavior now. If I start reading during the day, I get sleepy! What I like
about reading is that it gives me an opportunity to stop thinking about the
day, put all of that aside and focus on something else. Almost like “parking”
my brain for the evening.</p>
<!-- more -->
<p>I want a good book, not a great book. It needs to be something I enjoy, but
easy enough to put down. If it’s too good, it’s easy to lose track of time
and only notice the clock at 4am!</p>
<p>Currently I’m reading <a href="https://www.amazon.com/gp/product/B003YL4LYI">Dance with Dragons</a>,
the 5th book in the Game of Thrones series. It moves slowly, but it’s still
interesting. I’m not sure what I’m going to do if I finish this before the
6th book is out, which seems likely.</p>
<p>Here are a few other books I’ve enjoyed over the last few years.</p>
<p>The <a href="https://www.amazon.com/gp/product/B077L6GJWW">Expanse series by James S. A. Corey</a>, recommended by my friend Richie. The books are good, a little too good at
times, and have caused a late night or two. The
<a href="https://www.amazon.com/Dulcinea/dp/B018BZ3SCM">TV show</a> does a good job
representing the books.</p>
<p><a href="https://www.amazon.com/One-Way-S-J-Morden-ebook/dp/B076ZJMHWQ">One Way</a> by
SJ Morden (and the sequel <a href="https://www.amazon.com/gp/product/B07DHKHZZG">No Way</a>),
was an interesting read. I’d call it a crime drama, mixed with Mars, and a
little dystopian capitalism thrown in for good measure. I had a hard time
putting it down.</p>
<p>The <a href="https://www.amazon.com/dp/B074CHRWQM">Nexus Trilogy by Ramez Naam</a>,
according to Amazon, I powered through those books in about 6 days each. I
don’t really remember the storyline, but I do remember enjoying them.</p>
<p>And finally my kryptonite, <a href="https://www.amazon.com/Clive-Cussler/e/B000APJ4L6/">Clive Cussler</a>
books, specifically the <a href="https://www.amazon.com/dp/B074C3D5NF">Oregon Files</a>
series. Honestly these books are like eating candy to me. They’re so easy to
read, but they offer no “nutritional” value whatsoever! They’re super
formulaic, the action is always on, there’s little character development,
but damn, they’re fun!</p>
<p>Some of the other books I’ve enjoyed:</p>
<ul>
<li><a href="https://www.amazon.com/gp/product/B074ZGGVVC">The Stowaway</a> by Laurie Gwen
Shapiro (non-fiction) I thought this was fun, about a boy who tries to
stowaway on a ship to Antarctica when the first explorers were heading down.</li>
<li><a href="https://www.amazon.com/gp/product/B01A4ATWJU">Bellevue: Three Centuries of Medicine and Mayhem at America’s Most Storied Hospital</a>
by David M. Oshinsky (historical) A fascinating look at Bellevue Hospital
in NYC, and a little history of medicine. Next time you’re in NYC, go visit
Washington Square Park, it’s actually a cemetery for many of NYC’s yellow
fever victims.</li>
<li><a href="https://www.amazon.com/gp/product/B07DN8BQMD/">Children of Time</a> by Adrian
Tchaikovsky (sci-fi)</li>
<li><a href="https://www.amazon.com/gp/product/B075V8LBPP">Mars Trilogy</a> by Kim Stanley
Robinson (sci-fi)</li>
<li><a href="https://www.amazon.com/gp/product/B000QJLQZI">Alexander Hamilton</a> by Ron
Chernow (biography) This is an academic book, neat to learn some of the
actual history, but really heavy. There were nights I didn’t even finish a
paragraph before I closed the book to go to sleep. In the end, it took me
almost a year to finish!</li>
<li><a href="https://www.amazon.com/gp/product/B01N0SGUQC">The Desert and the Sea: 977 Days Captive on the Somali Pirate Coast</a>
by Michael Scott Moore (non-fiction)</li>
</ul>
Seamless/GrubHub order history2019-08-07T00:00:00Zhttps://petelepage.com/blog/2019/08/seamless-grubhub-order-history/<p class="lead">AKA How much have I spent on Seamless this year?</p>
<p>I use <a href="https://seamless.com/">Seamless</a> (aka GrubHub) a lot, probably 4 or 5
times a week. Don’t get me wrong, I can cook, and I like to cook, but also
knowing that yummy food is going to show up at your door in about 30 minutes
is amazing. And their new Progressive Web App is great, loads fast, it’s
installable, everything I want in a PWA.</p>
<p>But it got me thinking, how much do I spend on Seamless? They don’t
really tell you on their site. But, you can see your order history for the
last year. So, I wrote a little code to add up all my orders.</p>
<p>First, you’ll need to navigate to
<a href="https://www.seamless.com/account/history?pageSize=200">https://www.seamless.com/account/history?pageSize=200</a>,
essentially the order page, but showing 200 orders. If you see multiple
pages at the bottom, up the page size until everything is on the same page.</p>
<p>Once the page has loaded, open Chrome’s DevTools, switch to the Console
panel, and paste the following code:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">let</span> total <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span><br /><span class="token keyword">const</span> orders <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /><span class="token keyword">const</span> elems <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'ghs-past-order-card'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />elems<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">card</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">const</span> elem <span class="token operator">=</span> card<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'div.s-row > div'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /> <span class="token comment">// Date</span><br /> <span class="token keyword">const</span> d <span class="token operator">=</span> elem<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'div.s-row'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'div'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>textContent<span class="token punctuation">;</span><br /> <span class="token comment">// Restaurant</span><br /> <span class="token keyword">const</span> r <span class="token operator">=</span> elem<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'h4'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>textContent<span class="token punctuation">;</span><br /> <span class="token comment">// Total</span><br /> <span class="token keyword">const</span> t <span class="token operator">=</span> elem<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'h4'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>textContent<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token string">'$'</span><span class="token punctuation">,</span> <span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token comment">// Add order to array</span><br /> orders<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>d<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">,</span><span class="token regex-delimiter">/</span><span class="token regex-flags">g</span></span><span class="token punctuation">,</span> <span class="token string">''</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">,</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>r<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">,</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>t<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>d <span class="token operator">!==</span> <span class="token string">'CANCELED'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> total <span class="token operator">+=</span> <span class="token function">parseFloat</span><span class="token punctuation">(</span>t<span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>orders<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">'\n'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'count'</span><span class="token punctuation">,</span> orders<span class="token punctuation">.</span>length<span class="token punctuation">)</span><span class="token punctuation">;</span><br />console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'total'</span><span class="token punctuation">,</span> total<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>That should print off your list of orders, along with the total number of
orders, and the amount of money you’ve spent on Seamless/GrubHub. You can
also copy/paste the list as a CSV file if you want that.</p>
<p>Since August 22nd, 2018, I’ve ordered from Seamless 164 times. I’m going to
pull the orders into a spreadsheet to see what my favorite restaurants are
and such later, but this felt like a little bit of fun in the meantime.</p>
<div class="alert alert-primary" role="alert">
This code will break the moment they change their HTML or CSS rendering,
none of the elements are identified in any semantic way, essentially I
had to pick elements by position or relation to other elements.
</div>
<p>Enjoy!</p>
<p>PS: If you haven’t tried Seamless, use my <a href="http://fbuy.me/nutji">referral code</a>
and we both get a little bonus!</p>
Is my PWA installed?2019-07-18T00:00:00Zhttps://petelepage.com/blog/2019/07/is-my-pwa-installed/<div class="alert alert-primary" role="alert">
<b>Update:</b> This post is out of date.
See <a href="https://web.dev/customize-install/#detect-launch-type">Detect how the PWA was launched</a>
on web.dev for details on how to check if your PWA is installed, how it was
launched and if the user switches between <code>standalone</code> and a
<code>browser tab</code> view.
</div>
<p>I got a question via email this morning asking if there was a way for a PWA
to check if it’s installed. The site wanted to know what state the app was
running in, either a browser tab, or a standalone window.</p>
<p>First, you can use the
<a href="https://web.dev/get-installed-related-apps/"><code>getInstalledRelatedApps()</code></a> API
to check if your PWA is already installed.</p>
<p>You can check what ‘state’ the PWA is running in (browser tab, or standalone
window) with a little JavaScript to check to see if the
<a href="https://developers.google.com/web/fundamentals/app-install-banners/#detect-mode">CSS display mode is <code>standalone</code></a>.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">if</span> <span class="token punctuation">(</span>window<span class="token punctuation">.</span><span class="token function">matchMedia</span><span class="token punctuation">(</span><span class="token string">'(display-mode: standalone)'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>matches<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'display-mode is standalone'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<p>I also recommend you listen for the <code>appinstalled</code> event, it’s fired when
the user has installed your app, whether from your install promotion, or from
Chrome.</p>
<pre class="language-js"><code class="language-js">window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'appinstalled'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">evt</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'a2hs installed'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>For more details on how to customize the install experience of a
Progressive Web App, check out the <a href="https://web.dev/pwa/">PWA docs</a> on web.dev.
And for patterns to promote the installation of your PWA, check out
<a href="https://web.dev/promote-install/">Patterns for Promoting PWA Installation</a>.</p>
Google I/O Device Lab2014-07-07T00:00:00Zhttps://petelepage.com/blog/2014/07/devlab/<p>One of my highlights from Google I/O this year was the Device Lab that
<a href="https://plus.google.com/+MattGaunt/posts">Matt Gaunt</a> and I built to show
developers how their site looks across the multi-device web. It was a really
cool thing to see all kinds of different sites working on phones, phablets,
tablets, computers and even TVs.</p>
<img src="https://petelepage.com/assets/io14-devlab.jpg" class="full-width" />
<p>A few folks have asked how we set things up, and how we did it, so I figured
I’d document our process here!</p>
<h2 id="device-selection">Device selection <a class="direct-link" href="https://petelepage.com/blog/2014/07/devlab/#device-selection">#</a></h2>
<p>We ended up with 46 different devices on the wall, including:</p>
<ul>
<li><a href="https://play.google.com/store/devices/details/Nexus_4_8GB?id=nexus_4_8gb&hl=en">Nexus 4</a>
(x2)</li>
<li><a href="https://play.google.com/store/devices/details?id=nexus_5_black_16gb">Nexus 5</a>
(x10)</li>
<li><a href="https://play.google.com/store/devices/details?id=nexus_7_16gb_2013">Nexus 7 2013</a> (x4)</li>
<li><a href="https://play.google.com/store/devices/details?id=nexus_10_32gb">Nexus 10</a>
(x8)</li>
<li><a href="https://play.google.com/store/devices/details?id=sony_z_ultra">Sony Z Ultra Google Play Edition</a> (x2)</li>
<li><a href="https://play.google.com/store/devices/details?id=htc_one">HTC One (2013) Google Play Edition</a> (x2)</li>
<li><a href="https://play.google.com/store/devices/details?id=htc_m8">HTC One M8 Google Play Edition</a> (x2)</li>
<li><a href="https://play.google.com/store/devices/details?id=lg_g_pad">LG G Pad 8.3 Google Play Edition</a> (x2)</li>
<li><a href="https://play.google.com/store/devices/details?id=samsung_galaxy_s4">Samsung Galaxy S4 Google Play Edition</a>
(x2)</li>
<li><a href="http://www.amazon.com/Samsung-SM-G900H-Factory-Unlocked-International/dp/B00J4TK4B8">Samsung Galaxy S5</a> (x2)</li>
<li><a href="http://www.motorola.com/us/FLEXR1-1/Moto-X/FLEXR1.html">MotoX</a> (x2)</li>
<li><a href="http://www.apple.com/ipod-touch/">iPod Touch</a> (x1)</li>
<li><a href="http://www.apple.com/ipad-air/">iPad Air</a> (x1)</li>
<li><a href="http://www.apple.com/ipad-mini/">iPad Mini</a> (x1)</li>
<li><a href="http://promos.asus.com/us/chrome-os/chromebox/">ChromeBox</a> (x1)</li>
<li>Android TV (x1)</li>
</ul>
<p>We picked our devices mostly from <a href="https://play.google.com/store/devices/collection/promotion_50000c5_partner_us">Google Play Edition devices</a>, and picked a few
other fun and shiney net devices that would look cool on the wall. If you’re
considering building something like this for your company or team, look at your
analytics to understand what your users are using, and then regularly add new
devices as the usage changes. In most cases we had at least two of every device,
so that we could have one in portrait and one in landscape.</p>
<h2 id="network-connection">Network connection <a class="direct-link" href="https://petelepage.com/blog/2014/07/devlab/#network-connection">#</a></h2>
<p>When we ran the 46+ devices in the office prior to I/O, everything ran
beautifully, but we knew with all the attendees, each with at least one,
potentially two or three devices, the network would be a bit of a challenge.</p>
<p>We had hoped to use OTG Y-cables to both power and connect the devices to a
wired network, but the cables we got gave us a network connection, but no power
to the device, which means the screens wouldn’t stay on. So, at I/O, we
connected all of the devices via WiFi to a dedicated access point with it’s own
SSID, that way we could ensure that the devices were connecting to that access
point, as opposed to one potentially several hundred feet away or even on
another floor.</p>
<p>We tweaked a few settings on the Android devices to optimize the network
connection. For example we disabled <em>Avoid poor network connections</em> and
disabled <em>Wi-Fi optimization</em> to keep things working as well as possible.</p>
<h2 id="power">Power <a class="direct-link" href="https://petelepage.com/blog/2014/07/devlab/#power">#</a></h2>
<p>We kept all of the devices powered via USB at all times so that we could keep
the screens on and also didn’t have to worry about recharging things. Rather
than using the individual wall warts, we picked up a bunch of <a href="http://www.amazon.com/gp/product/B00IBDOB5I">Anker 40W 5-Port
USB power supplies</a>. At full
screen brightness, the N10s suck as much power from the USB port as they use,
<a href="http://www.amazon.com/MagNector-N10-Nexus-Pogo-Cable/dp/B00B2166BS">Pogo cables</a>
provided more.</p>
<h2 id="screens">Screens <a class="direct-link" href="https://petelepage.com/blog/2014/07/devlab/#screens">#</a></h2>
<p>To keep the screens on and to prevent dimming, we had to enabled <em>Stay awake</em> in
the <em>Developer options</em> panel, that ensured that was long as they were plugged
in, the screen would stay on. We also installed <a href="https://play.google.com/store/apps/details?id=it.android.keepscreenon">Keep Screen On
LITE</a>,
which prevented the screens from dimming after a period of time.</p>
<h2 id="attaching-devices-to-the-wall">Attaching devices to the wall <a class="direct-link" href="https://petelepage.com/blog/2014/07/devlab/#attaching-devices-to-the-wall">#</a></h2>
<p>This was the sort of easy part, we just used good ol’ velcro! To get the cool
looking pattern, we cut out stencils of each device using colored paper, then
taped them to the wall and kept rearranging them until we got the look we
wanted. Once we knew where everything was going to go, then we started
sticking them up.</p>
<h2 id="pushing-urls-to-the-devices">Pushing URLs to the devices <a class="direct-link" href="https://petelepage.com/blog/2014/07/devlab/#pushing-urls-to-the-devices">#</a></h2>
<p>The back end that sits on Compute Engine, and runs a little node app that pushes
URLs out to the devices using Google Cloud Messaging. You can grab the source
for the <a href="https://github.com/GoogleChrome/MiniMobileDeviceLab">Mini Mobile Device Lab from
GitHub</a> and give it a shot
yourself. Big props to <a href="https://plus.google.com/+MattGaunt/posts">Matt</a> for
doing most of the work here. We have a few ideas on how we can make this easier
to set up, and potentially allow you to run it without any kind of back end
infrastructure. More on that later 😉</p>
<h3 id="android-devices">Android Devices <a class="direct-link" href="https://petelepage.com/blog/2014/07/devlab/#android-devices">#</a></h3>
<p>Each device has a simple Cordova app running on it that listens for a Google
Cloud Message, when it gets that message, it fires an intent to start the
browser and open the URL in the message. This meant we can not only open
Chrome, but any browser that’s installed on the device, Firefox, Opera, Android
browser, etc.</p>
<p>With 40+ Android devices connected, we found out that the Play store gets a
little cranky when there are too many devices connected to the same email
address, so we ended up have to create three of four different email accounts
that we used to sign into the devices and install the software.</p>
<h3 id="ios-and-chromeos">iOS and ChromeOS <a class="direct-link" href="https://petelepage.com/blog/2014/07/devlab/#ios-and-chromeos">#</a></h3>
<p>Since the software we used depending on Google Cloud Messaging and an app that
fired Android Intents, we had to roll a different solution for non-Android
devices. We wrote a simple AppEngine app that used the Channel API to push
messages out to the devices. On the device, we opened a “background” page that
listened for a message/URL and then simply did a <code>window.open()</code>.</p>
<h3 id="android-tv">Android TV <a class="direct-link" href="https://petelepage.com/blog/2014/07/devlab/#android-tv">#</a></h3>
<p>We built a custom one-off WebView app that had two WebViews running, one that
was always hidden, connected to the AppEngine app we used for iOS and ChromeOS,
and the one that showed the URL sent.</p>
A web UI for my Pi2013-08-14T00:00:00Zhttps://petelepage.com/blog/2013/08/a-web-ui-for-my-pi/<p>My project this weekend on my <a href="https://github.com/petele/PiHomeControl">home automation system</a> was two-fold, first I wanted to clean up the code and make it a bit more object oriented, but I also wanted to add a web interface that is accessible outside my apartment.</p>
<p>The largest part of the weekend was spent re-architecting things. Now, each component is effectively self-contained, so it will be easier to add or remove components later and making it easier for other people to use. Once that was done, I dug into the web interface. The Pi does a POST to an AppEngine app every 30 seconds (configurable) with the status of all of the devices lights, air conditioners, door, even the Harmony remote. Since the data changes often, is less than about 10k and I don’t need it stored for any considerable length, I decided to just store it in memcache to make retrieving it faster.</p>
<p>On the client side, I wanted to emulate the look of the <a href="https://petelepage.com/blog/2013/08/a-web-ui-for-my-pi/targus.com/us/productdetail.aspx?regionId=7&sku=AKP10US">Targus Keypads</a> that I’ve got throughout my place acting as light switches, which is why you see the layout as it is. Across the top is the status of the different devices, for example the state is AWAY and the front door is closed. Both the living room (LR) and bedroom (BR) air conditioners are off, the temperature inside and out is 81° and the amp is off.</p>
<p>The buttons control things in the apartment, the red and blue buttons are modifier keys that affect the gray buttons. For example, pushing Off then Kitchen turns the kitchen lights off. The plus and minus keys only affect the air conditioners right now, though they used to also dim the lights. The buttons show as depressed when that item or set of lights are turned on.</p>
<p>Right now I’m simply doing an XMLHTTPRequest on a setInterval to refresh the data, but I’m planning to modify it to use the Channels API in the near future which will help to eliminate some of the existing lag. I’m also trying to decide if it’s worth adding a live webcam view, not sure I’d really use it, so I’m thinking not, but who knows.</p>
<p>The next part of the project is to solder up the <a href="http://www.adafruit.com/products/1110">Adafruit RGB Negative 16x2 Keypad Kit</a> and turn that into the alarm clock beside my bed. Not only would it wake me up in the morning, it would also turn the lights on and turn the stereo on to FM radio.</p>
Home Automation For Geeks2013-07-09T00:00:00Zhttps://petelepage.com/blog/2013/07/home-automation-for-geeks/<p>I’ve always had a fascination with home automation systems, things that make your life easier and computers that do the stuff that I’m too lazy to do. In college, I had my tiny little apartment in Ottawa all wired up with <a href="http://en.wikipedia.org/wiki/X10_(industry_standard)">X10</a> and this weekend, I “finished” my most recent creation. Though honestly, is it ever really done?</p>
<p>It all started a few months ago when I picked up a set of the Philips Hue light bulbs - they’re amazing. LED light bulbs that are fully addressable and programmable via a simple to use <a href="http://developers.meethue.com/">REST API</a>. The biggest problem I had with them was that to really use them, you had to leave the light switch On and turn the lights on and off via the app. But it gets to be a small pain in the butt if you have to pull your phone out of your pocket every time you want to turn a light bulb on or off.</p>
<h2 id="the-kit">The Kit <a class="direct-link" href="https://petelepage.com/blog/2013/07/home-automation-for-geeks/#the-kit">#</a></h2>
<ul>
<li>1 x <a href="http://www.adafruit.com/products/998">Raspberry Pi (Model B)</a></li>
<li>1 x <a href="http://www.adafruit.com/products/375">Simple Door Swtich</a></li>
<li>1 x <a href="http://www.adafruit.com/products/381">Temperature Sensor</a></li>
<li>1 x <a href="http://www.adafruit.com/products/64">Half-Sized Bread Board</a></li>
<li>1 x <a href="http://www.amazon.com/Global-Cache-IP2IR-iTach-Wired/dp/B003BFTKUC/">iTach IP to IR</a></li>
<li>1 x <a href="http://www.logitech.com/en-us/product/harmony-ultimate?crid=60">Harmony Ultimate Remote Control</a></li>
<li>1 x <a href="http://www.amazon.com/Philips-431643-Personal-Wireless-Frustration/dp/B00BSN8DN4/">Philips Hue Starter Kit</a></li>
<li>(a few) x <a href="http://www.amazon.com/Philips-431650-Personal-Wireless-Frustration/dp/B00BSN8DLG/">Philips Hue Single Bulbs</a></li>
<li>4 x <a href="http://www.amazon.com/Targus-AKP10US-Numeric-Keypad/dp/B002NURRL0/">Targus Numeric USB Key Pad</a></li>
<li>2 x Window Air Conditioners with IR remotes (already had these)</li>
</ul>
<h2 id="okay%2C-so-what%E2%80%99s-it-do%3F">Okay, so what’s it do? <a class="direct-link" href="https://petelepage.com/blog/2013/07/home-automation-for-geeks/#okay%2C-so-what%E2%80%99s-it-do%3F">#</a></h2>
<p>The Raspberry Pi is effectively the brains of the apartment, it keeps simple state, and sends commands to the lights, the iTach and the Harmony hub. The keypad are placed throughout the apartment and act like multi-function light switches. The zero key and the enter key have special meaning though, either putting the system into Away mode or Home mode. Away mode is just a simple macro that turns all of the Hue lights, uses the iTach to turn off the air conditioners off, shuts the TV and stereo off via the Harmony Hub, and then waits until the front door opens again. When the front door opens, a magnetic door switch saves me from having to hit the Home button which runs another simple macro to turns the lights on, and depending on both the inside and outside temperature, turns the air conditioners on. Oh, and it also turns one of the lights in the living room purple when I have an unread message on Google Voice.</p>
<h2 id="building-out-the-system">Building out the system <a class="direct-link" href="https://petelepage.com/blog/2013/07/home-automation-for-geeks/#building-out-the-system">#</a></h2>
<p>Building out the system, some parts were easier than others. The API for the Philips Hue lights, <em>awe-some</em>! The iTach to control the air conditioners, it was good once I figured out how to teach it IR commands. Google Voice, yah, there’s no API there - that required a little thinking. And the Harmony Hub, there’s no published API for the Harmony Ultimate Hub, and wow, that one sucked.</p>
<p>My original plan was the write a <a href="http://developer.chrome.com/apps/about_apps.html">Chrome Packaged App</a> to handle the lights, and run a few USB numeric keyboards around my place. I figured the cost of leaving a Chromebook running 24x7 would be acceptable with the energy savings I was getting from the lights. But, I kept hitting a single and pretty simple problem, I couldn’t keep the Chromebook from locking. I could prevent it from going to sleep, but it still locked, leaving the the USB keyboards useless, since all they could do was type passwords into the machine. So, I pulled the Raspberry Pi I ordered months ago out of a drawer and started fiddling; within a few hours I had a working prototype - I was stoked.</p>
<h2 id="the-un-official-google-voice-api">The <strong>un-official</strong> Google Voice API <a class="direct-link" href="https://petelepage.com/blog/2013/07/home-automation-for-geeks/#the-un-official-google-voice-api">#</a></h2>
<p>To be clear, there isn’t a Google Voice API available to developers (boo!), though there are a few good libraries out there that are worth checking out. Sadly, if you have two factor authentication turned on, none of them work since they depend on sending your username and password to Google and doing some unholy magic to log in. And if you don’t have two factor turned on, <strong>please <a href="http://www.mattcutts.com/blog/google-two-step-authentication/">go turn on two factor authentication</a> RIGHT NOW</strong>. I’ll wait. No seriously, I’ll wait.</p>
<p>I was pretty resigned to not being able to integrate a new message indicator to the system after spending a few days trying to figure out if there was any way possible around the two factor stuff, or if I could somehow figure out how to make a web request with the right cookies. That is of course, until I was reading the history of browsers, and was reminded of <a href="http://en.wikipedia.org/wiki/Lynx_(web_browser)">Lynx</a>, the first browser I used. Did it still exist, would it work? Would it run JavaScript? The answer is yes! Sure enough, I installed Lynx on my Pi, and then tried logging into Google Voice. I figured if I could log in, I should be able to somehow scrape the results. Sure enough, it worked. Now to figure out how to scrape some results!</p>
<p>After a little searching and some Chrome DevTools digging, I found Google Voice has a JSON end point that will give you a simple JSON object with message counts:</p>
<p><strong><a href="https://www.google.com/voice/request/unread">https://www.google.com/voice/request/unread</a></strong></p>
<pre class="language-js"><code class="language-js"><span class="token punctuation">{</span><br /> <span class="token string-property property">"unreadCounts"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token string-property property">"all"</span><span class="token operator">:</span> <span class="token number">3</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"inbox"</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"missed"</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"placed"</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"received"</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"recorded"</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"sms"</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"spam"</span><span class="token operator">:</span> <span class="token number">28</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"trash"</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"unread"</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"voicemail"</span><span class="token operator">:</span> <span class="token number">3</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"r"</span><span class="token operator">:</span> <span class="token string">"SomeMagicCodeHere"</span><br /><span class="token punctuation">}</span></code></pre>
<p>And <strong>BOOM</strong> I was off! Unfortunately, it means I have to fork a process, start Lynx, request the URL and then parse the result every time I want to check if I have new messages. And my cookies do expire so I have to log back in every so often to re-authenticate, but it’s better than not having any reporting at all!</p>
<h2 id="ip-to-ir-with-the-itach">IP to IR with the iTach <a class="direct-link" href="https://petelepage.com/blog/2013/07/home-automation-for-geeks/#ip-to-ir-with-the-itach">#</a></h2>
<p>A while ago, I came across the iTach IP 2 IR controller, it’s an interesting little device, I think mostly meant for high end home automation systems, but it wasn’t that expensive and I figured I’d give it a go. It’s pretty simple, it has a network jack, and three 1/8" jacks on the back. The 1/8" jacks connect to IR emitters that you can either place in the immediate vicinity of a device, or connect an IR blaster and just put in the room. The <a href="http://www.globalcache.com/files/docs/API-iTach.pdf">manual</a> is pretty thorough for it, except the left out all of the important intro stuff, like the difference between a blaster and an emitter, or where the IR learning port was. Oh, and they don’t have a Mac app, so you need to grab one from a <a href="http://www.rmartijnr.eu/">third party app</a> to learn commands.</p>
<p>Once I got this guy somewhat figured out, the rest was pretty easy. It sends out regular UDP multicast packets so you can find it on the network, and then you communicate with by opening a TCP socket and sending an IR command to one of the three IR ports.</p>
<p>For example, to set the bedroom air conditioner to 72°, you’d connect to the iTach on port 4998 and send:</p>
<pre class="language-text"><code class="language-text">sendir,1:3,1,37993,1,1,319,160,21,61,21,21,21,21,21,21,21,61,21,21,21,<br />21,21,21,21,21,21,21,21,61,21,21,21,61,21,21,21,21,21,21,21,61,21,61,<br />21,21,21,61,21,21,21,21,21,61,21,21,21,61,21,21,21,21,21,61,21,3799</code></pre>
<p>The <code>sendir</code> part is pretty self explanatory, followed by <code>1:3</code> which tells the iTach to send it to the third IR port on the device. I guess some of their devices can have multiple addresses, explaining the first <code>1</code>. If all goes according to plan, it should then respond with:</p>
<pre class="language-text"><code class="language-text">completeir,1:3,1</code></pre>
<p>I mentioned the system turns the air conditioners on depending on the inside and outside temperature. I used the <a href="http://www.adafruit.com/products/381">DS18B20 temperature sensor</a> from Adafruit and followed their <a href="http://learn.adafruit.com/adafruits-raspberry-pi-lesson-11-ds18b20-temperature-sensing?view=all">awesome tutorial</a> for setting it up. For the outside temperature, I check a weather station in Brooklyn (it’s appearently closest to my place) via <a href="http://www.wunderground.com/weather/api/">WeatherUnderground</a>. Their API is free to use and super simple if you’re just using it for a personal project and not hitting it very hard.</p>
<h2 id="harmony-hub-api">Harmony Hub API <a class="direct-link" href="https://petelepage.com/blog/2013/07/home-automation-for-geeks/#harmony-hub-api">#</a></h2>
<p>The crowning achievement came this weekend when I figured out how to query and address my Harmony Ultimate Hub. Logitech doesn’t make an API available to developers, and in some ways, I don’t blame them - Harmony remotes are pretty complex and there’s a lot of state and other stuff involved. But that wasn’t going to stop me.</p>
<p>If you’re not familiar with the Ultimate, it’s pretty sweet, not only does the remote control everything, there’s a little ‘hub’ that sits in your living room and allows you to use your phone or tablet as an additional remote anytime you’re on your network.</p>
<p>Sadly, searching for Harmony Hub API at the time revealed nothing useful, I tried up, down and ten ways to Sunday to see if anyone else was trying to do what I did. I couldn’t imagine I was the only one! But nothing. So I did what any developer would do, first I port scanned it (it’s got an open port on 5222, and 8088). I tried my damn’est on port 8088, it responded to HTTP POST requests only, but I couldn’t ever get a useful response. Then I hooked up a <a href="http://www.wireshark.org/">packet sniffer</a> and tried to see what was going on with the app. Nothing. Nodda.</p>
<p>Ah right, the wireless network I have setup, was preventing my Mac from seeing packets sent from my phone to the hub. Grrr! Okay, share the Mac’s network and then try again. This time it can’t find the hub. Right, different subnet. Long story short, connect to hub, switch network, now I can see a few packets. Great, so now I can see a few packets, let’s do a few searches to see if anyone has posted about:</p>
<pre class="language-text"><code class="language-text">vnd.logitech.harmony/vnd.logitech.harmony.engine</code></pre>
<p>Again, BINGO! A GitHub repository called <a href="https://github.com/jterrace/pyharmony/">pyharmony</a>, complete with a great <a href="https://github.com/jterrace/pyharmony/blob/master/PROTOCOL.md">protocol guide</a> and working code. The API uses <a href="http://en.wikipedia.org/wiki/XMPP">XMPP</a>, which makes sense when you figure the hub potentially needs to update multiple devices with it’s current state in near real time. While I would have much preferred an REST API, I figured I could work with XMPP.</p>
<p>I grabbed the code, installed the pre-reqs, then ran it. Queue <a href="http://www.sadtrombone.com/?play=true">sad trombone sound</a>. Didn’t work, well, it connected but then hung while trying to get the session token. I went back and forth with the other developer a few times, compared outputs, and then realized, we were dealing with different hubs.</p>
<p>So this weekend, back to the WireShark I went, this time capturing everything, and sure enough, the login credentials are subtly different, once I updated the code and used the correct login credentials, it worked like a charm. Sadly, my credentials don’t work on his Hub, so we still need to figure out how to do proper device detection and use the appropriate credentials.</p>
<p>You can grab my forked code for pyharmony at <a href="https://github.com/petele/pyharmony/">https://github.com/petele/pyharmony/</a>, which includes the credentials for the Harmony Ultimate Hub. If you’re using one of the older Hubs, grab Jeff’s code. I’ve also added a few additional functions to my fork that aren’t in the original, including <code>getCurrentActivity</code> and <code>startactivity</code>.</p>
<p>In my home automation system, the Away macro and the Bed Time macro both check the current activity and if the system is on, turns everything off by calling <code>startactivity</code> with the activity ID <strong>-1</strong>. The <code>getConfig</code> API returns a JSON object with all of the info about your system, including the activity IDs for everything you’ve programmed into your Harmony. Obviously I could add a bunch more functionality to this, but that’s for another day.</p>
<h2 id="energy-efficient">Energy Efficient <a class="direct-link" href="https://petelepage.com/blog/2013/07/home-automation-for-geeks/#energy-efficient">#</a></h2>
<p>Talking to some of my co-workers about the system, they were a little concerned about the power consumption of leaving the Pi on all day every day and if that would eliminate the benefit of the Hue lights. As I was wrapping things up this weekend, I pulled out my trusty Kill-a-watt, and did some quick measurements. The Pi uses only about 2 watts, 3 if it’s really pushing it. The lights are only about 8.5 watts each, and since the air-conditioners only come on when needed, I’m being smarter about power there too! So overall, I’m pretty sure that this will save me a little money in the long run.</p>
<h2 id="home-automation-for-everyone">Home Automation for Everyone <a class="direct-link" href="https://petelepage.com/blog/2013/07/home-automation-for-geeks/#home-automation-for-everyone">#</a></h2>
<p>There’s a whole bunch more that I want to do with my system, for example a morning alarm, connected to my calendar to make sure I get up in time for any meetings. It could turning the lights on, tune the radio to my favorite station gently turning the sound up (until I get out of bed), the possibilities are endless.</p>
<p>And with the price of hardware like the Raspberry Pi, door switches and such, anyone with a little geek know how can put together a pretty awesome system. Feel free to grab my code and rip it apart and do your own thing with it, it’s not exactly pretty, but I love the fact my apartment welcomes me home at night and says Goodbye when I leave.</p>
High DPI: Tips and Tricks2013-05-22T00:00:00Zhttps://petelepage.com/blog/2013/05/high-dpi-tips-and-tricks/<p>During the presentation that <a href="https://plus.google.com/115120259572077332394/posts">John Mellor</a> and I did at <a href="https://developers.google.com/events/io/2013/">I/O</a> this year covering building beautiful websites for high DPI displays, we summarized our talk into about 7 key points.</p>
<p>If you follow these seven simple guidelines, you’ll find your site looks great on any high DPI display.</p>
<ul>
<li>Setting <code>width=device-width</code> means you only have to care about <span style="color: #0da861;">device independent pixels</span></li>
<li>If you don’t set the viewport to <code>width=device-width</code>, or if you use a <span style="color: #f44a3f;">fixed width</span>, you’re in a <span style="color: #f44a3f;">world of hurt</span>.</li>
<li>The <code>devicePixelRatio</code> on high DPI devices can range from <span style="color: #4387fd;">1.3 to 3</span> and is about <span style="color: #4387fd;">more than just phones or tablets</span>, there are laptops too!</li>
<li>Use <span style="color: #0da861;">vector images</span> wherever possible</li>
<li>Use <code>@media</code> queries to specify appropriate background images</li>
<li><span style="color: #0da861;">Highly compressed 2x</span> images work well in many cases</li>
<li>For sharp <code>canvas</code> images, beware of <code>webkitBackingStorePixelRatio</code></li>
</ul>
<p>You can find the video on YouTube at <a href="http://youtu.be/alG-UwRWV_U">http://youtu.be/alG-UwRWV_U</a>, and we’ve also posted the slides at <a href="http://goo.gl/j5Z5W">http://goo.gl/j5Z5W</a>.</p>
Seeing the world through high DPI2013-05-21T00:00:00Zhttps://petelepage.com/blog/2013/05/seeing-the-world-through-high-dpi/<p>At <a href="https://developers.google.com/events/io/2013/">Google I/O</a> this year every attendee got a brand new, beautiful Chromebook Pixel - for many, it was their first high DPI (a so called retina) laptop, and I think many people realized how, um, not so beautiful 1x images look on a 2x screen. In other words, how crappy most of the web looks on a high DPI display.</p>
<p>While there, <a href="https://plus.google.com/115120259572077332394/posts">John Mellor</a> and I gave a talk covering the best practices for building sites that look good on high DPI displays. We covered the theory behind the three different types of pixels and how browsers display 1x images on 2x displays, and the best practices for making sure your site looks great on any device, no matter what if it’s a regular screen or a high DPI display.</p>
<p>You can check out the video below or on <a href="http://youtu.be/alG-UwRWV_U">YouTube</a>, and we’ve also posted the <a href="http://goo.gl/j5Z5W">slides</a>.</p>
<iframe width="616" height="347" src="http://www.youtube.com/embed/alG-UwRWV_U" frameborder="0" allowfullscreen=""></iframe>
Viewport target-densitydpi support is being deprecated2013-02-21T00:00:00Zhttps://petelepage.com/blog/2013/02/viewport-target-densitydpi-support-is-being-deprecated/<p>Support for <code>target-densitydpi</code> in the <code>viewport</code> meta tag was recently <a href="http://trac.webkit.org/changeset/119527">removed from WebKit</a> and with Chrome for Android moving forward to current revisions on WebKit, this change is now rolling out in Android. This change affects only a small number of sites because of the limited implementation of the <code>target-densitydpi</code> attribute. It brings Chrome and other WebKit based browsers in compliance with the <a href="http://dev.w3.org/csswg/css-device-adapt/#viewport-meta">specification</a> and matches the behavior of other modern mobile browsers.</p>
<p>In order to to best understand what’s changed, you need to remember that a device pixel is not the same as a CSS pixel (see <a href="http://coding.smashingmagazine.com/2012/08/20/towards-retina-web/">CSS Pixels</a>), and that high DPI displays are able to create crisper and sharper images by fitting more device pixels into a smaller space. This means that in order for content to appear at a normal size, the browser treats each CSS pixel as multiple device pixels and the browser scaled up assets and images to fit within the correct number of CSS pixels.</p>
<p>In Android browser and early versions of Chrome for Android, developers could use <code>target-densitydpi=device-dpi</code> viewport value to force the browser to make a CSS pixel the same size as a device pixel, which may cause content to appear incorrectly scaled on screen (as seen in figure 1).</p>
<h2 id="an-easy%2C-quick-fix">An Easy, Quick Fix <a class="direct-link" href="https://petelepage.com/blog/2013/02/viewport-target-densitydpi-support-is-being-deprecated/#an-easy%2C-quick-fix">#</a></h2>
<p>In most cases, if your site is affected by this change you can fix it easily by serving the same mark-up (including viewport) to Chrome for Android as you serve to mobile Safari (which never supported <code>target-densitydpi</code>).</p>
<h2 id="best-practices-for-modern-mobile-web-sites">Best Practices for Modern Mobile Web Sites <a class="direct-link" href="https://petelepage.com/blog/2013/02/viewport-target-densitydpi-support-is-being-deprecated/#best-practices-for-modern-mobile-web-sites">#</a></h2>
<p>When designing a new mobile site, or updating existing pages, you should use modern techniques for dealing with high DPI displays; including always using <code><meta name="viewport" content="width=device-width"></code> and a flexible layout for mobile sites. Remember, device sizes, orientations and pixel ratios vary which means that your site may be displayed on a screen ranging from 320 to over 600 CSS pixels wide.</p>
<p>For more information about best practices for building websites that work well on high DPI displays, check out <a href="https://twitter.com/kaishin">Reda Lemeden’s</a> <a href="http://coding.smashingmagazine.com/2012/08/20/towards-retina-web/">Towards a Rentina Web</a> article on Smashing Magazine.</p>
<h2 id="one-other-little-note">One Other Little Note <a class="direct-link" href="https://petelepage.com/blog/2013/02/viewport-target-densitydpi-support-is-being-deprecated/#one-other-little-note">#</a></h2>
<p>While writing up this post, I accidentally did a search for sites that used <code>target-density</code> instead of <code>target-densitydpi</code> and I came across quite a few of them. If you’re using <code>target-density</code> (without the dpi at the end), you can just remove it, it wasn’t doing anything!</p>
Meta viewport syntax - comma or semi-colon?2013-02-14T00:00:00Zhttps://petelepage.com/blog/2013/02/meta-viewport-syntax-comma-or-semi-colon/<p><strong>TL;DR:</strong> If you’re using a semi-colon to separate values in your viewport meta tag, your site may break! Use a comma instead.</p>
<p>Browsers can be pretty forgiving most days, they do their best to fix our coding mistakes; but it’s a tough job. Most days, they manage to get it right, but trying to fix all the possible error cases is pretty hard. Worse yet, sometimes one browser fixes our code for us, while others don’t.</p>
<p>The viewport meta tag seems to be one of those elements that’s a little bit persnickety. The <a href="http://dev.w3.org/csswg/css-device-adapt/">CSS Device Adaptation spec</a> says the correct syntax uses a comma to separate the values. Unfortunately some sites use a semi-colon, which causes the browser to ignore your viewport settings completely!</p>
<h2 id="correct-syntax">Correct Syntax <a class="direct-link" href="https://petelepage.com/blog/2013/02/meta-viewport-syntax-comma-or-semi-colon/#correct-syntax">#</a></h2>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width, initial-scale=1.0<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></code></pre>
<h2 id="incorrect-syntax">Incorrect Syntax <a class="direct-link" href="https://petelepage.com/blog/2013/02/meta-viewport-syntax-comma-or-semi-colon/#incorrect-syntax">#</a></h2>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width; initial-scale=1.0<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></code></pre>
<p>If you want to see this in action and how bad things can look, use the remote debugger with Chrome for Android Beta to change the comma to a semi-colon in the viewport meta tag on <a href="http://google.com/">Google.com</a>. The site will suddenly go from looking good, to looking awful.</p>
<div style="clear:both;"></div>
<p>The Developer Tools also warn you in the Console when it comes across an invalid viewport meta tag with the warning: <code>Viewport argument value "device-width;" for key "width" is invalid, and has been ignored. Note that ';' is not a separator in viewport values. The list should be comma-separated.</code></p>
<p>Go make awesome, but use a comma!</p>
Using meta viewport to optimize rendering on mobile devices2012-12-12T00:00:00Zhttps://petelepage.com/blog/2012/12/using-meta-viewport/<h2 id="what-is-the-viewport%3F">What is the viewport? <a class="direct-link" href="https://petelepage.com/blog/2012/12/using-meta-viewport/#what-is-the-viewport%3F">#</a></h2>
<p>Imagine if every web page you opened on your phone or tablet got the same version as your desktop, and was displayed on the small screen of your device. On a phone, that might mean you would see only the top leftmost corner, a tiny 320px by 480px view, and you’d have to scroll around the page to be able to see everything. (See figure 1). Unlike a desktop or laptop, there is no way to resize the browser window to fit the content that you want to see. Thankfully, on mobile devices, almost every modern browser scales and renders the page so that you can more easily see and interact with it, panning and zooming into the areas you’re most interested in.</p>
<p>Most mobile browsers define a default viewport width of 980px CSS pixels, meaning that they lay out the page on a 980px wide area, then scale it down to fit within the width of the screen. (If you’re unfamiliar with CSS pixels, device pixels, device pixels ratios, or the like, check out the Smashing Magazine article <a href="http://coding.smashingmagazine.com/2012/08/20/towards-retina-web/">Towards a Retina Web</a>. This works great for most desktop sites that don’t take into account mobile devices and were designed for big screens. Sites that were designed to be compatible with narrow screens however are likely to be unnecessarily stretched and scaled. For example, see figure 2 - there is no viewport set, and the image is 1600px wide, that means users need to scroll left to right to see all of the content.</p>
<p>The meta viewport tag allows you to override the browser default value, allowing you to adjust the display and zoom level to best suit your web page.</p>
<h2 id="using-meta-viewport">Using meta viewport <a class="direct-link" href="https://petelepage.com/blog/2012/12/using-meta-viewport/#using-meta-viewport">#</a></h2>
<h3 id="setting-a-specific-viewport-width">Setting a specific viewport width <a class="direct-link" href="https://petelepage.com/blog/2012/12/using-meta-viewport/#setting-a-specific-viewport-width">#</a></h3>
<p>To override the default viewport defined by the browser you simply need to add a viewport meta tag in the <code><head></code> section of your page:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=320<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></code></pre>
<p>Setting the viewport width to 320px, is similar to resizing your laptop browser window to be 320px wide, now the browser will render the page on a canvas that’s 320px wide. If your device is 320px, then you’re all set, the browser doesn’t have to do any scaling and you’re pretty much all set. Obviously not all devices are 320px wide, and your site most likely isn’t 320px wide.</p>
<p>But what happens if you rotate the phone from portrait to landscape, and suddenly the device is now actually 480px wide? If this happens, the browser scales the content up by 50%, so that the 320px width, so that the 320px wide document is stretched to fit in the 480px wide browser window. Unfortunately, when this scaling is applied, less content will be displayed above the fold, and the content may look oversized.</p>
<p>If you’ve designed a site for a fixed width desktop, for example, you know your page is 600px wide, then you can simply add <code><meta name="viewport" content="width=600" /></code> to the <code><head></code> of your document, which will scale the content to fit within the window. Depending on the density of your content, this may work well, but you’ll want to test it to make sure.</p>
<p>Adding a specific width viewport, is discouraged unless you’re adding it to a legacy site as a stop-gap measure until you can build a more responsive site.</p>
<h3 id="setting-the-viewport-width-to-the-device-width">Setting the viewport width to the device-width <a class="direct-link" href="https://petelepage.com/blog/2012/12/using-meta-viewport/#setting-the-viewport-width-to-the-device-width">#</a></h3>
<p>Consider the sustainability of a fixed width website and how it would look given the huge variety of mobile web devices, each with a different screen size, it becomes even more of a challenge when those devices are used in landscape mode. For that reason, building your site using a responsive approach and placing it within the viewport without any zooming will help to make your site look great across all of those devices. It doesn’t need to be responsive from phone all the way to desktop, but it should at least flex from a small portrait phone, to a mid-sized mobile tablet in landscape; about 320px to 640px.</p>
<p>When you design your page or web app using a responsive approach, you don’t want the browser to render your responsive content in a viewport that is 980px wide and then scaled down to the tiny screen; instead you want the content to render in the actual viewport size. To do that, you can tell the browser to set the viewport to be the width of the actual device by applying the following meta viewport tag:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></code></pre>
<p>This means the viewport will be proportional to the width of the device’s screen, for example on any portrait iPhone (whether or not it is retina) the width would be 320 <a href="http://coding.smashingmagazine.com/2012/08/20/towards-retina-web/">CSS pixels</a>, on a landscape iPhone 3 or iPhone 4 it would be 480 CSS pixels, on a portrait Nexus 4 it would be 384 CSS pixels, and on a landscape Nexus 4 it would be 640 CSS pixels</p>
<h3 id="setting-the-viewport-scale">Setting the viewport scale <a class="direct-link" href="https://petelepage.com/blog/2012/12/using-meta-viewport/#setting-the-viewport-scale">#</a></h3>
<p>When you specify a specific value for the viewport width, the browser will automatically determine the appropriate scale factor needed. For example, if you set the <code>width=640</code> on a device that has a screen width of 320px, the browser will scale the content by 0.5.</p>
<p>In some cases, you may want to specify the scale used by the browser. You can do this by setting <code>initial-scale</code>, <code>minimum-scale</code> and <code>maximum-scale</code>.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width, initial-scale=1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre>
<p>Initial scale forces the browser to an initial scale, but still allows the user to pinch-zoom on the content to be able to make things larger or smaller. By using <code>minimum-scale</code> or <code>maximum-scale</code>, you can set a boundary on the amount of scaling the user can do.</p>
<p>You can also prevent zooming by adding <code>user-scalable=no</code>, or setting the <code>minimum-scale</code> and <code>maximum-scale</code> to the same values. This is particularly useful for mobile web applications where it doesn’t make sense to allow the user to zoom or scale content.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre>
<p>Currently the user-scalable property does not work in Chrome for Android (m18).</p>
<h3 id="setting-the-viewport-height">Setting the viewport height <a class="direct-link" href="https://petelepage.com/blog/2012/12/using-meta-viewport/#setting-the-viewport-height">#</a></h3>
<p>In addition to setting the viewport width, you can also set the viewport height. While it’s not something that’s used often, it’s good for mobile web applications that may pan horizontally only.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>height=device-height<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre>
<h2 id="practices-that-are-no-longer-recommended">Practices that are no longer recommended <a class="direct-link" href="https://petelepage.com/blog/2012/12/using-meta-viewport/#practices-that-are-no-longer-recommended">#</a></h2>
<ul>
<li>Adding a specific width viewport, is discouraged unless you’re adding it to a legacy site as a stop-gap measure until you can build a more responsive site (you’ll need to replace the 320 with the width of your site): <code><meta name="viewport" content="width=320" /></code></li>
<li>The viewport property <code>target-densitydpi</code> has been removed from the <a href="http://dev.w3.org/csswg/css-device-adapt/">CSS Device Adaptation</a> specification and should not be used. It has been deprecated in Chrome for Android, and has never been supported in Mobile Safari or Firefox for Android. For more information, see <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=737090">Mozilla Bug 797090</a> or <a href="https://bugs.webkit.org/show_bug.cgi?id=88047">WebKit bug 88047</a>.</li>
</ul>
<h2 id="best-practice-for-using-meta-viewport">Best practice for using meta viewport <a class="direct-link" href="https://petelepage.com/blog/2012/12/using-meta-viewport/#best-practice-for-using-meta-viewport">#</a></h2>
<ul>
<li>Use a <strong>comma</strong> to delimit different viewport properties. Although semi-colons may work, they do not behave consistently across browsers.</li>
<li>For a responsive website, designed with mobile in mind and where the user may want to be able to zoom in to particular areas of content:<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width, initial-scale=1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre>
</li>
<li>For a mobile web application where you don’t want to allow zooming of the content:<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre>
</li>
</ul>
<h2 id="the-future">The Future <a class="direct-link" href="https://petelepage.com/blog/2012/12/using-meta-viewport/#the-future">#</a></h2>
<p>Currently, the only consistent way to specify the viewport for a page is via the meta viewport tag, but the W3C is currently working on a <a href="http://dev.w3.org/csswg/css-device-adapt/">CSS Device Adaptation</a>, that will provide a way to specify the size, zoom factor and orientation of the viewport in CSS using the <code>@viewport</code> rule. As of late 2012, only Opera Mobile 11+ and Internet Explorer 10 provide support for it using a vendor prefix.</p>
<h2 id="additional-resources">Additional Resources <a class="direct-link" href="https://petelepage.com/blog/2012/12/using-meta-viewport/#additional-resources">#</a></h2>
<ul>
<li><a href="https://dev.opera.com/articles/view/an-introduction-to-meta-viewport-and-viewport/">An Introduction to Meta Viewport and @viewport</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Viewport_concepts">MDN Viewport Concepts</a></li>
<li><a href="https://www.quirksmode.org/mobile/viewports.html">A tale of two viewports</a></li>
</ul>
Thinking about the offline web2012-04-17T00:00:00Zhttps://petelepage.com/blog/2012/04/thinking-about-the-offline-web/<p>Over the last few months, I’ve spent a lot of time thinking about how to make it easier for developers to build web applications that work offline, its a tough problem to solve. The web has the features/technology to do it, but I think there are two things preventing us from getting there easily. The first is a perception issue for users, many people think that the web only works when they’re online, no internet, no web. That leads to a bit of a catch-22, users aren’t asking for it, so developers aren’t prioritizing it. The second problem is that it requires a change in the way we build web applications. Instead of building server side web applications, we need to build more client side web applications that use web-based APIs to get what they need.</p>
<p><strong>More than just ‘offline’</strong></p>
<p>But applications that work offline isn’t the only reason to start using these new techniques. There are lots of other scenarios that benefit from the offline experience. For example, the intermittent connection where someone may have a connection for 5 minutes, then lose it for 2 then have it again for 3 minutes. The low bandwidth connection, for people connected maybe by dial-up or 3G modem. It also benefits the person with a great net connection - because any time you load data from the local machine, it’s almost guaranteed to be faster than if it were to come across the network. The performance benefits are pretty significant here!</p>
<p><strong>Reuse the same code for different platforms</strong></p>
<p>If you’re already building native (iOS, Android, Desktop) applications, you probably already have the set of server-side APIs, build your web application to take advantage of those to get the data that it needs. Architect your web application in the same way that you’re building native applications - using the same set of APIs and XMLHTTPRequests! If you’re worried about performance (and everyone should be), you should cache data locally, so that after the first time the app is run, it only needs to get the newest data from the server, and there is some available immediately at start up.</p>
<p><strong>Offline techniques</strong></p>
<p>Building applications that work offline requires two main things, a way to store the applications components (the HTML, JavaScript, CSS, images, and other assets) on the user’s machine, and some way to store data. Solving the first problem is done with the HTML5 feature application cache, which tells the browser to explicitly store the necessary files locally on the user’s computer, and instead of asking for them from the server every time, load them from the cache.</p>
<p>HTML5 offers lots of great ways to cache data locally, localStorage, indexed DB and web SQL. Unfortunately, the only consistently available storage feature today is localStorage, but it’s got a few drawbacks. WebSQL works across most browsers, but it has been deprecated, and could go away at some point. The recommended method, is indexed DB, but it’s not available in all browsers yet, but it’s coming. Check out <a href="http://caniuse.com/">http://caniuse.com</a> for more info about implementation.</p>
<p><strong>Up next</strong> - how do we get users to demand offline?</p>
Happy Blue Beanie Day2011-11-30T00:00:00Zhttps://petelepage.com/blog/2011/11/happy-blue-beanie-day/<h2 id="happy-blue-beanie-day-from-the-html5-hipster">Happy Blue Beanie Day from the HTML5 Hipster <a class="direct-link" href="https://petelepage.com/blog/2011/11/happy-blue-beanie-day/#happy-blue-beanie-day-from-the-html5-hipster">#</a></h2>
<p><a href="https://petelepage.com/img/HTML5-Guy-BBeanie.png"><img src="https://petelepage.com/img/HTML5-Guy-BBeanie-300x300.png" alt="HTML5 Guy with Blue Beanie" title="HTML5 Guy with Blue Beanie" /></a></p>
<p><strong>Credits:</strong> <a href="http://sethladd.com/">Seth Ladd</a> for the HTML5 Guy idea. <a href="http://www.bearskinrug.co.uk/">Kevin Cornell</a> (via) <a href="http://www.zeldman.com/2009/10/25/toque-o-the-morning/">Zeldman</a> for the Beanie.</p>
<p><strong>Other Formats:</strong> <a href="https://petelepage.com/img/HTML5-Guy-BBeanie.svg">SVG</a> <a href="https://petelepage.com/img/HTML5-Guy-BBeanie.ai">Adobe Illustrator</a> <a href="https://petelepage.com/img/HTML5-Guy-BBeanie.png">PNG</a></p>
Building A More App-y Web2011-11-17T00:00:00Zhttps://petelepage.com/blog/2011/11/building-a-more-app-y-web/<p>Everyone wants “apps” these days, on their phone, their tablets, Apple, has the App Store to sell apps for the Mac. The demand is coming from all over the place, including consumers who have no idea what apps are, to people who are seeing this as a new revenue opportunity (which, it is)! I even heard from one developer who said that his boss came to him all panicked because he got a call from the company’s chairman of the board, wanting to know what their app strategy was. So far, most of the focus has been on apps for mobile devices, tablets, but thankfully it’s really starting to take off on the web, and we’re seeing more web apps appearing.</p>
<p>One piece of the puzzle that hasn’t really solidified yet is what defines a web app, and how does it differ from a web site? Are <em>app like experiences</em> on the web part of a website, or are they web apps? What about web sites that provide the same functionality as an app, but looks like a web site?</p>
<h2 id="is-it-a-web-site%2C-or-a-web-app%3F">Is it a web site, or a web app? <a class="direct-link" href="https://petelepage.com/blog/2011/11/building-a-more-app-y-web/#is-it-a-web-site%2C-or-a-web-app%3F">#</a></h2>
<p><a href="https://petelepage.com/assets/tripit_web.png"><img src="https://petelepage.com/assets/tripit_web-300x219.png" alt="" title="tripit_web" /></a></p>
<p>TripIt is a great example, it’s concept is absolutely an app, it helps me to coordinate my travel plans into a single place where I can easily organize my trips into a single online experience. Here’s where it gets confusing, compare their web application to their tablet application. I can do exactly the same sets of things, but one looks like an application, and one looks like a website. They both let me do the same tasks, but they provide very different experiences to complete those tasks. So what is the TripIt web app, is it a site or an app?</p>
<p><a href="https://petelepage.com/assets/tripit_ipad2.png"><img src="https://petelepage.com/assets/tripit_ipad2-300x225.png" alt="" title="tripit_ipad2" /></a></p>
<div class="well">Before I go too far though, I want to profess my love for TripIt, I use it for all my travel and have been a Pro user for over a year now, and will renew next time it's up for renewal. I love that it tells me where I can find my luggage, how to get to my next gate, and often, it knows my flight is going to be late before the airline tells me! I love that I can carry it on my phone and quickly look up my flight confirmation code, or when I'm in a foreign country, pull up my hotel information hand my phone to the taxi driver and point to where I want to go.</div>
<h2 id="how-do-we-define-a-web-app-then%3F">How do we define a web app then? <a class="direct-link" href="https://petelepage.com/blog/2011/11/building-a-more-app-y-web/#how-do-we-define-a-web-app-then%3F">#</a></h2>
<p>Many people have suggested different ways to define web apps in a single test or definition. I enjoy reading <a href="http://web.appstorm.net/">AppStorm</a>, I think they do a great job of highlighting awesome web apps, but a while ago, they tried to define web apps, and provided one of my favorite <a href="http://web.appstorm.net/general/opinion/what-is-a-web-app-heres-our-definition/">non-answers</a>:</p>
<blockquote>
<p>A web application is an application utilizing web and [web] browser technologies to accomplish one or more tasks over a network, typically through a [web] browser.</p>
</blockquote>
<p>That answer is almost as good as saying “because it’s not orange” to the question why is the sky blue.</p>
<h2 id="it-can%E2%80%99t-be-a-single-criteria">It can’t be a single criteria <a class="direct-link" href="https://petelepage.com/blog/2011/11/building-a-more-app-y-web/#it-can%E2%80%99t-be-a-single-criteria">#</a></h2>
<p>Single criteria definitions don’t work for me either. Can we say something is an app because of a single property? For example, creation vs. consumption, or architecture. Add the diversity of web apps to the mix, and I think we hit a wall when it comes to defining web apps with a single criterion.</p>
<h2 id="a-web-app-checklist">A web app checklist <a class="direct-link" href="https://petelepage.com/blog/2011/11/building-a-more-app-y-web/#a-web-app-checklist">#</a></h2>
<p>The diversity of apps makes it impossible to have a single go/no-go checklist that we can use to define apps. A criterion for one app may be completely inappropriate for another app. Instead, I think we need to judge apps based on reaching a particular bar of ‘app-y-ness’.</p>
<ul>
<li>Does the app have a tight focus around a single point or purpose?</li>
<li>Is the app self-contained, keeping me within the app to complete my task?</li>
<li>Does the app encourage interaction, engagement and completion of tasks?</li>
<li>Does the app look beaultiful and have a rich user experience?</li>
<li>Does the app have an action oriented interface that uses similar paradigms to native applications, for example dialogs, buttons instead of links and new pages?</li>
<li>Is there at least a functional offline experience?</li>
<li>Does the app take advantage of the capabilities of the device, like geolocation or device motion?</li>
<li>Are traditional websites elements and links are hidden from view?</li>
<li>Is the app architected to uses a primarily client side architecture model?</li>
</ul>
<p>Now, let’s look at TripIt’s web “app” vs. their tablet app:</p>
<table class="table">
<colgroup>
<col />
<col />
<col /></colgroup>
<tbody>
<tr>
<td></td>
<td>Web "app"</td>
<td>Tablet app</td>
</tr>
<tr>
<td>Tight focus</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Self-contained</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Encourage interaction, engagement & task completion</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Beautiful & rich user experience</td>
<td>Maybe</td>
<td>Yes</td>
</tr>
<tr>
<td>Action oriented with native design paradigms</td>
<td>No</td>
<td>Yes</td>
</tr>
<tr>
<td>Offline</td>
<td>No</td>
<td>Yes</td>
</tr>
<tr>
<td>Native capabilities</td>
<td>No</td>
<td>No</td>
</tr>
<tr>
<td>Traditional website elements hidden?</td>
<td>No</td>
<td>Yes</td>
</tr>
<tr>
<td>Client side architecture</td>
<td>No</td>
<td>Yes</td>
</tr>
</tbody>
</table>
<p>By this criteria, even though their web site is super functional, I can get the things done that I want and has some of the qualities of an app, it clearly could be more app-y. Could TripIt build the same experience as a web app? Absolutely! Heck, it might even be less work because then they could reuse a lot of their design and UI components across the web and tablet versions.</p>
<h2 id="but-who-really-cares%3F">But who really cares? <a class="direct-link" href="https://petelepage.com/blog/2011/11/building-a-more-app-y-web/#but-who-really-cares%3F">#</a></h2>
<p>I do! With more and more people demanding apps, I think we’re missing a huge opportunity to provide our users with web applications that are better, faster, and more fun to use than their mobile or native counterparts. People will pay for mobile and native applications, but we have given them very few web apps that are worth their money. <strong>We need to step up and change that, let’s build a more app-y web.</strong></p>
HTML5 Guy + Gelaskin2011-11-16T00:00:00Zhttps://petelepage.com/blog/2011/11/html5-guy-gelaskin/<p>By popular request - a few little additions to the HTML5 Guy!</p>
<p><a href="https://petelepage.com/img/HTML5-Guy.png"><img src="https://petelepage.com/img/HTML5-Guy.png" class="w-100" /></a></p>
<p>Now, with in <a href="https://petelepage.com/img/HTML5-Guy.svg">SVG</a>! I also created a <a href="https://petelepage.com/img/gela1.png">skin</a> for my laptop with this guy and had it printed at <a href="http://www.gelaskins.com/">GelaSkins</a>:</p>
<p><a href="https://petelepage.com/img/gela1.png"><img src="https://petelepage.com/img/gela1.png" class="w-100" /></a></p>
Happy Friday from the HTML5 Guy2011-11-04T00:00:00Zhttps://petelepage.com/blog/2011/11/html5guy/<p><a href="https://petelepage.com/img/HTML5-Guy.png"><img src="https://petelepage.com/img/HTML5-Guy.png" class="w-100" /></a></p>
Canadian Chrome Web Store Incentives2011-10-31T00:00:00Zhttps://petelepage.com/blog/2011/10/canadian-chrome-web-store-incentives/<p><a href="https://petelepage.com/assets/flag_canada3.jpg"><img src="https://petelepage.com/assets/flag_canada3-300x212.jpg" alt="" title="flag_canada3" /></a></p>
<p>The last month has been really exciting for the <a href="http://bit.ly/tcqrrG">Chrome Web Store</a>. Not only did we launch the new (and very much improved user experience), we also launch internationally. The Chrome Web Store is available in lots more countries, but one in particular is close to my heart - Canada. In fact, I was up in Canada last week for a <a href="http://webapphackmtl.eventbrite.com/">hack-a-thon</a> and to meet with some great companies to talk about the Chrome Web Store.</p>
<p>With any newly launched product, there are lots of great opportunities for people to jump on board early, and if you’re a Web Developer in Canada, this is certainly one of them. On Thursday, we announced a special program for Canadian Web Developers. We want to see more Canadian Web Developers featured with apps that will resonate with Canadians. That’s why we’re offering a <a href="http://bit.ly/s4O0Fn">great incentive</a> for submitting your apps! Our panel of experts will review the apps that you submit and for each app that <a href="http://bit.ly/sT3UzT">meets the criteria</a>, you will receive a $200 Amazon gift certificate - not to mention exposure to more than 200 million Chrome users around the world.</p>
<p><img src="https://petelepage.com/assets/cws-bag.png" alt="Chrome Web Store - Shopping Bag" title="Chrome Web Store" /></p>
<p>You’ve got until December 31, 2011 at midnight (PST) to get your app submitted to the Chrome Web Store and fill out the submission form. You’ve got just about 2 months to get your app built and submitted. Be sure to read the complete rules and regulations at <a href="http://bit.ly/sT3UzT">here</a>.</p>
<p> </p>
<p> </p>
<p>To learn more visit <a href="http://bit.ly/s4O0Fn">The Chrome Web Store Canadian Web Developer Incentive Program</a> site, including full regulations, and how to submit your app!</p>
<p>If you’re looking for resources to get you started, check out some of these links:</p>
<ul>
<li><span class="Apple-style-span" style="line-height: 18px;"><strong>Chrome Web Store Developer Guides</strong>: <a href="http://code.google.com/chrome/webstore/docs/index.html">http://code.google.com/chrome/webstore/docs/index.html</a>. These are the developer guides and tutorials for getting started submitting web apps to the Chrome Web Store.</span></li>
<li><span class="Apple-style-span" style="line-height: 18px;"><strong>Tutorial: Getting Started with the Chrome Web Store:</strong> <a href="http://code.google.com/chrome/webstore/docs/get_started_simple.html">http://code.google.com/chrome/webstore/docs/get_started_simple.html</a>. A super easy tutorial that will get your app into the store, without any custom features.</span></li>
<li><span class="Apple-style-span" style="line-height: 18px;"><strong>Background: Extending Your App’s Life:</strong> <a href="http://code.google.com/chrome/apps/docs/background.html">http://code.google.com/chrome/apps/docs/background.html</a>. This details how to add the background features to your app, using this, you can query for information, and then use notifications to alert the user, even when they’re not in/on your site.</span></li>
<li><span class="Apple-style-span" style="line-height: 18px;"><strong>Using the Notifications API:</strong> <a href="http://www.html5rocks.com/en/tutorials/notifications/quick/">http://www.html5rocks.com/en/tutorials/notifications/quick/</a> is a tutorial on how to use the notifications API available in Chrome and through the Chrome Web Store.</span></li>
<li><span class="Apple-style-span" style="line-height: 18px;"><strong>Identifying the User:</strong> <a href="http://code.google.com/chrome/webstore/docs/identify_user.html">http://code.google.com/chrome/webstore/docs/identify_user.html</a> is a good overview of OpenID and at the bottom under resources are a couple links to tutorials on implementing OpenID.</span></li>
<li><span class="Apple-style-span" style="line-height: 18px;"><strong>AppMator:</strong> <a href="http://appmator.appspot.com/">http://appmator.appspot.com</a> is a tool that will automate the basic process of getting your app into the store. Sadly, it won’t handle the background page stuff, but does do the basics</span></li>
<li><span class="Apple-style-span" style="line-height: 18px;"><strong>Application Cache:</strong> <a href="http://www.webdirections.org/blog/get-offline/">http://www.webdirections.org/blog/get-offline/</a> and <a href="http://www.html5rocks.com/en/tutorials/#appcache">http://www.html5rocks.com/en/tutorials/#appcache</a> both provide great resources that you’ll find quite helpful</span></li>
<li><span class="Apple-style-span" style="line-height: 18px;"><strong>Web Storage - LocalStorage and SessionStorage:</strong> are both widely implemented and allow for storing of name/value pairs, not good for binary data, but useful for things like lessons completed or topics of interest. The other great way we’ve seen this used is as a temporary cache until the client can connect to the server to update things. <a href="http://www.html5rocks.com/en/features/storage">http://www.html5rocks.com/en/features/storage</a></span></li>
</ul>
<p>Good luck!</p>
<p>PEte</p>
The Anatomy of a Great Chrome Web Store Listing (Updated)2011-09-29T00:00:00Zhttps://petelepage.com/blog/2011/09/the-anatomy-of-a-great-chrome-web-store-listing-updated/<p><a href="https://petelepage.com/assets/cws-bag.png"><img src="https://petelepage.com/assets/cws-bag.png" alt="Chrome Web Store - Shopping Bag" title="Chrome Web Store" /></a></p>
<p>A couple of months ago, I blogged about the <a href="https://petelepage.com/blog/2011/06/the-anatomy-of-a-great-chrome-web-store-listing.html">anatomy of a great Chrome Web Store listing</a> - and it looks like it’s time to do an update. A couple of weeks ago, we changed the UI on the developer dashboard and started asking for new screen shots, and promotion images. Not only is the new UI a little easier to use and understand, but soon we’ll be launching a new user experience for consumers - which will require new assets from you. I’d strongly recommend updating your assets sooner than later to make sure that when we do roll out the new UI, you’re ready for it, and are looking great.</p>
<p>Since the consumer UI will change in the near future, I’ve listed my suggestions in the order that they appear within the Developer Dashboard and included screenshots from both the <a href="https://chrome.google.com/webstore/developer/dashboard">Developer Dashboard</a> and the current consumer UI. I’ll update the screenshots when the new UI launches.</p>
<h2 id="let%E2%80%99s-dive-in">Let’s dive in <a class="direct-link" href="https://petelepage.com/blog/2011/09/the-anatomy-of-a-great-chrome-web-store-listing-updated/#let%E2%80%99s-dive-in">#</a></h2>
<p>Your first chance to make a great impression with a new user is often on your application listing page in the <a href="https://chrome.google.com/webstore">Chrome Web Store</a>. People will make a split second decision whether to install your application based on what you put on there, so it’s important to make a great first impression. Let’s take a look at what makes a great Chrome Web Store application listing. This updated guide highlights a couple key components of a great Chrome Web Store listing. I tried not to repeat content from the documentation, but instead tease out the important parts or best practices. Be sure to follow the links for additional resources or details.</p>
<h2 id="a-couple-quick-examples">A Couple Quick Examples <a class="direct-link" href="https://petelepage.com/blog/2011/09/the-anatomy-of-a-great-chrome-web-store-listing-updated/#a-couple-quick-examples">#</a></h2>
<p><a href="https://petelepage.com/assets/tweetdeck.png"><img src="https://petelepage.com/assets/tweetdeck-300x214.png" alt="" title="TweetDeck listing" /></a><a href="https://petelepage.com/assets/goog-maps.png"><img src="https://petelepage.com/assets/goog-maps-300x208.png" alt="" title="Google Maps listing" /></a></p>
<p>I think both <a href="http://bit.ly/oPfKaI">TweetDeck</a> and <a href="http://bit.ly/oEYMpj">Google Maps</a> do a great job of providing a compelling, and interesting listing, so we’ll use those as our primary examples.</p>
<h2 id="application-name-and-short-description">Application Name and Short Description <a class="direct-link" href="https://petelepage.com/blog/2011/09/the-anatomy-of-a-great-chrome-web-store-listing-updated/#application-name-and-short-description">#</a></h2>
<p><a href="https://petelepage.com/assets/appname.png"><img src="https://petelepage.com/assets/appname-300x96.png" alt="" title="Google Maps CWS Header" /></a>The application manifest file (<a href="http://bit.ly/oxope3">manifest.json</a>) has a property for a short description of your application. Use this to tell users about what your application does and why they should get excited and care about it. It’s limited to 132 characters, and cannot include any HTML. For example, the description for Angry Birds is simply “Birds! Slingshots! Destruction! Feathers! Fun!” For Gmail, it’s “Fast, searchable email with less spam.”</p>
<p>You must also specify the application name within the manifest file - be sure to specify only the name and not any taglines or things that would be more appropriate for the short description. In the current UI, the +1 button will also be displayed in this section, another reason to not include tag lines or other stuff in your application name.</p>
<p>One other good tip - try to use ‘simple’ characters in your application name, and don’t get too fancy including special unicode characters. For example, if your app is named App<sup>2</sup>, use App2 instead of App<sup>2</sup>. Currently search sees those two as different app names, and if you search for App2, you won’t find App<sup>2</sup>.</p>
<h2 id="detailed-description">Detailed Description <a class="direct-link" href="https://petelepage.com/blog/2011/09/the-anatomy-of-a-great-chrome-web-store-listing-updated/#detailed-description">#</a></h2>
<p>Beyond the concise description set in the manifest file, a detailed description can be provided via the Chrome Web Store Developer Dashboard. This is your space to really excite your potential users about your application and convince them to install or buy it. If you’ve already built a landing page on your web site, you can likely leverage what you’ve already written here.<a href="https://petelepage.com/assets/description.png"><img src="https://petelepage.com/assets/description-300x136.png" alt="" title="Chrome Web Store App Description" /></a></p>
<p>You should provide a detailed description of your application, including it’s features, quotes from great reviews, information about recent updates, and any other information that might be relevant to potential users. If your application has a freemium or free trial, be sure to tell users the differences between the paid and free experience so they’re not surprised or disappointed by functionality that they might not have access to.</p>
<p>Remember, this description will be searchable, so be sure to use any key words in your description that might help users to find your application if they try searching for it without knowing the name of it.</p>
<p>If your product is compatible with a Google product, make reference to that Google product by using the text “for”, “for use with”, or “compatible with”, and be sure to include the ™ symbol with the Google trademark. Example: “for Google Chrome™”. For other third party products, be sure to check any requirements that they may have when describing your application.</p>
<p>I also recommend including a short description of things that have changed since the last update, including any major bug fixes and such. This helps keep the user informed and tells them that you’re invested in the app and listening to their feedback.</p>
<h2 id="supplying-icons">Supplying Icons <a class="direct-link" href="https://petelepage.com/blog/2011/09/the-anatomy-of-a-great-chrome-web-store-listing-updated/#supplying-icons">#</a></h2>
<p><a href="https://petelepage.com/assets/maps-icon.png"><img src="https://petelepage.com/assets/maps-icon.png" alt="" title="Google Maps application icon" /></a>When you package and upload your application to the Chrome Web Store, it needs to include at least the manifest file, and an application icon. The icon that you include in the ZIP file is the icon that is used on the new tab page in Chrome after the application has been installed. Your icon should be eye catching and representative of your application. You also need to supply a second icon that is uploaded via the Developer Dashboard. This icon is the one that is shown in the Chrome Web Store - through search, browse and currently on you application listing page.</p>
<p>Personally, I would use the same icon in both places because otherwise it I think it gets confusing for users, they see one icon when they install your application and then can’t find it in their new tab page because you’ve used a different icon!</p>
<p>Remember, the icons are pretty small, so you can’t be too complex or detailed. If your icon is not circular, we recommend you try to keep it within a 96x96 pixels box, with a 16 pixel per side transparent border. For circular icons, you can go up to 112x112 pixels. It’s generally a good idea to avoid words or text because it makes localization difficult and often becomes difficult to read. One exception to this is that if your app name or company name is part of your logo, then it’s probably okay to use.</p>
<p>Here are a couple of my favorite icons</p>
<p><a href="https://petelepage.com/assets/music.png"><img src="https://petelepage.com/assets/music.png" alt="" title="music" /></a><a href="https://petelepage.com/assets/goodfood.png"><img src="https://petelepage.com/assets/goodfood.png" alt="" title="goodfood" /></a><a href="https://petelepage.com/assets/word2.png"><img src="https://petelepage.com/assets/word2.png" alt="" title="word2" /></a></p>
<p>Check out the <a href="http://bit.ly/poes2F">Supplying Images</a> of the Chrome Web Store docs for more info.</p>
<h2 id="screenshots-%26-promotional-images">Screenshots & Promotional Images <a class="direct-link" href="https://petelepage.com/blog/2011/09/the-anatomy-of-a-great-chrome-web-store-listing-updated/#screenshots-%26-promotional-images">#</a></h2>
<p><a href="https://petelepage.com/assets/images.png"><img src="https://petelepage.com/assets/images-300x98.png" alt="" title="images" /></a>We’ve done some work to increase the quality and provide you with more space to show off what your app does with the new screenshots and promotional images. The old images were simply too small to be able to really be useful or exciting for consumers. We’ve also done away with the customized header background.</p>
<p>Screenshots can be one of the most compelling factors in a user’s decision to install or buy an application, so it’s important to provide at least four or five screen shots that give a user a sense for how your application works, some of the features it offers and an idea of the different elements. You should always start with a beautiful screen shot that provides a detailed overview of the application experience first, and then drill into the additional elements. Make sure the screenshots show your application in use, so that users can see how the application works, and how they might use it. If you’re displaying any kind of data from the user, be sure to use safe or sample data in the screenshots instead of blurring things out or accidentally sharing personally identifiable information.</p>
<p>Screen shots should be full bleed images with square corners that are 1280x800 pixels. Since not every user has a 30" display, you can count on these images being scaled smaller to fit within the Chrome Web Store UI, and the user’s screen. With such large images, it’s important to show your app both at a high level, but also include images of your app in detail. Be sure to not over-compress or scale images so that you can’t see any of the detail. Further details about supplying screenshots are available in the Chrome Web Store documentation under Supplying Images. You can also provide locale specific screenshots if your app supports multiple locales.</p>
<h3 id="youtube-videos-%26-google-docs-presentations">YouTube Videos & Google Docs Presentations <a class="direct-link" href="https://petelepage.com/blog/2011/09/the-anatomy-of-a-great-chrome-web-store-listing-updated/#youtube-videos-%26-google-docs-presentations">#</a></h3>
<p>A picture may be worth 1000 words, but video is worth many more. The Chrome Web Store allows you to add a YouTube video to the screen shot collection, allowing you to provide a richer way to show people your application, and convince them to install or buy it. Keep in mind that in many cases, people haven’t yet made the decision to install or buy your application, and likely won’t be willing to invest a lot of time, so optimize for a short, high quality overview that highlights the main features and will encourage people to try it.</p>
<h3 id="promotional-images">Promotional Images <a class="direct-link" href="https://petelepage.com/blog/2011/09/the-anatomy-of-a-great-chrome-web-store-listing-updated/#promotional-images">#</a></h3>
<p>We’re always on the lookout for great web applications to feature throughout the Chrome Web Store. When we find them we use the promotional images, but if you don’t have promotional images, we can’t promote you! Also in the new UI, the small and large tiles will be featured more prominently across the site for all applications. Only the small marquee is required, but I’d strongly recommend providing all three.</p>
<p>All three promotional images should use the same branding and identity as the rest of the images that you supplied, and have a dark or medium background with light text and avoid any white or very light elements along the edges of the images. The small tile should be 440x280 pixels, the large tile 920x680 pixels, and a marquee tile that’s 1400x560 pixels. All of the tiles should have square corners and with no padding. It’s also a good idea to Additional details on <a href="http://bit.ly/plBiS9">promotional images</a> can be found in the Chrome Web Store documentation.</p>
<h2 id="verified-domain">Verified Domain <a class="direct-link" href="https://petelepage.com/blog/2011/09/the-anatomy-of-a-great-chrome-web-store-listing-updated/#verified-domain">#</a></h2>
<p><a href="https://petelepage.com/assets/gojee.png"><img src="https://petelepage.com/assets/gojee-300x69.png" alt="" title="gojee" /></a></p>
<p>Verifying your domain via the <a href="http://bit.ly/qcybxL">Google Webmaster Tools</a> is like adding a certificate of authenticity to something that you’re going to buy in the store. A verified application tells potential users they’re about the install a genuine, user experience from your site-and that they’re not getting a cheap, or potentially unsafe knock-off. Also, be sure the domain name that you’ve verified and are using makes sense, and isn’t a development or staging domain; which may scare away your users.</p>
<h3 id="additional-links">Additional Links <a class="direct-link" href="https://petelepage.com/blog/2011/09/the-anatomy-of-a-great-chrome-web-store-listing-updated/#additional-links">#</a></h3>
<p><a href="https://petelepage.com/assets/morelinks.png"><img src="https://petelepage.com/assets/morelinks.png" alt="" title="morelinks" /></a>You can also provide your potential customers with additional resources that are on your site. Providing the URLs for description and support pages can improve your users’ experience and help make this item’s ratings and comments more meaningful. The “Link to website for your item” should point to the landing page of your website. You can also provide a link to the help or support section of your site via the “Link to support & FAQ for your item”</p>
<h2 id="categories">Categories <a class="direct-link" href="https://petelepage.com/blog/2011/09/the-anatomy-of-a-great-chrome-web-store-listing-updated/#categories">#</a></h2>
<p><a href="https://petelepage.com/assets/category.png"><img src="https://petelepage.com/assets/category.png" alt="" title="TweetDeck Category Listing" /></a>Putting your application into the most appropriate category will make it easy for users to find your application when they browse the store. You can choose up to two categories that are most appropriate for your application. You can find a <a href="http://bit.ly/qDDFUS">list</a> of the different categories as well as a description and examples for each in the Chrome Web Store developer documentation.</p>
<p>Even though you can choose up to two categories, and your application will be listed under both, it will only show one category on the navigation breadcrumb when you view it in the <a href="http://bit.ly/qiYc9d">Chrome Web Store</a>.</p>
<h2 id="other-helpful-components">Other Helpful Components <a class="direct-link" href="https://petelepage.com/blog/2011/09/the-anatomy-of-a-great-chrome-web-store-listing-updated/#other-helpful-components">#</a></h2>
<h3 id="version-number-%26-last-updated">Version Number & Last Updated <a class="direct-link" href="https://petelepage.com/blog/2011/09/the-anatomy-of-a-great-chrome-web-store-listing-updated/#version-number-%26-last-updated">#</a></h3>
<p><a href="https://petelepage.com/assets/morelinks.png"><img src="https://petelepage.com/assets/morelinks.png" alt="" title="morelinks" /></a>One of the great features of web applications is the ease at which we can add new features, fix bugs and enhance the overall user experience. It’s strongly recommended that you increment the version number in your manifest file and re-upload it to the Chrome Web Store any time you provide significant updates or bug fixes to your application. This ensures that the version number and last updated field on your application listing stay up to date, and users know that you’re application is fresh, and that you’re constantly working to make it better.</p>
<p>Also, when choosing a version number, it’s a good idea to start at 1.0, or as close to it as you can so that people know your application is ready for real world use and that you’re not something that’s still in beta or may not work as expected. If you’re not ready to be out of beta yet, that’s okay, but provide a realistic version number that indicates how close you are to being out of beta. And be sure to stay away from version numbers like 0.0.0.1.</p>
<h3 id="mature-content">Mature Content <a class="direct-link" href="https://petelepage.com/blog/2011/09/the-anatomy-of-a-great-chrome-web-store-listing-updated/#mature-content">#</a></h3>
<p>If your application isn’t very child friendly, then marking it as Mature Content is probably a good idea. For example, some first person shooter games may not be appropriate for children. When considering whether your application should be marked as mature or not, evaluate your entire application, including any user generated content that may appear. There’s more info about ratings in the Ratings Guidelines section.</p>
<h3 id="analytics">Analytics <a class="direct-link" href="https://petelepage.com/blog/2011/09/the-anatomy-of-a-great-chrome-web-store-listing-updated/#analytics">#</a></h3>
<p>While providing your Google analytics token won’t directly make the user experience any better, it will provide you with lots of data that you can use to understand who’s using your app, how many people are installing it and more detailed information than is available through the number of users or the number of weekly installs.</p>
<p><span class="Apple-style-span" style="font-size: 20px; line-height: normal;">Using the Chrome Web Store Badge</span></p>
<p><a href="https://petelepage.com/assets/ChromeWebStore_Badge_v2_206x58.png"><img src="https://petelepage.com/assets/ChromeWebStore_Badge_v2_206x58.png" alt="" title="ChromeWebStore_Badge_v2_206x58" /></a>On your application home page, you can let others know that your app is available in the Chrome Web Store by using the “<a href="http://bit.ly/qiBe4t">Available in the Chrome Web Store</a>” badge on your site and even going so far as to do browser sniffing to provide Chrome users with additional messaging.</p>
<h2 id="conclusion">Conclusion <a class="direct-link" href="https://petelepage.com/blog/2011/09/the-anatomy-of-a-great-chrome-web-store-listing-updated/#conclusion">#</a></h2>
<p>There’s a lot you can do to make sure that you make a great first impression with your Chrome Web Store listing that encourages users to buy or install your application. Good luck, and let’s go build some cool web apps! And of course, I’d be remiss if I didn’t include a link to <a href="http://bit.ly/nZ08Sd">@sethladd</a>’s <a href="http://bit.ly/nfA6G9">blog post</a> about 13 great tips!</p>
HTML5 Slide Decks & Presentation Timer2011-09-21T00:00:00Zhttps://petelepage.com/blog/2011/09/html5-slide-decks-presentation-timer/<p><a href="https://petelepage.com/assets/timer.png"><img src="https://petelepage.com/assets/timer.png" alt="Countdown Timer For Presentations" title="Presentation Timer" /></a></p>
<p>A couple of folks at GDD and other events have asked what I use to create my slide decks, and I figured I’d share and add a link to a little plug in that I wrote for the slide decks.</p>
<p>My slide decks are based on <a href="http://code.google.com/p/html5slides/">HTML5Slides</a>, the Google HTML5 Slide Template - it’s the same one that was used at Google IO, generally by the Chrome DevRel team and lots of other folks. It’s freely available for anyone to use, modify, fork, whatever you want, and creating the content for it is pretty easy. One of my favorite things about using the HTML5Slides as my presentation software is that I can embed my demos straight into the slides and when you want to understand what’s going on, you can simply view source!</p>
<p>There are a couple of things that I think this framework is missing though. There isn’t a way to do a presenter view and an audience view (not always necessary, but sometimes helpful to make sure you don’t forget any of the important points you want to hit). There wasn’t an easy way to add fades when doing builds, but most importantly for me, it doesn’t have any kind of timer. I love to talk, and can easily lose track of time when on stage.</p>
<h2 id="creating-a-presentation-timer">Creating a presentation timer <a class="direct-link" href="https://petelepage.com/blog/2011/09/html5-slide-decks-presentation-timer/#creating-a-presentation-timer">#</a></h2>
<p>This weekend, while on the flight from Brazil to Argentina, I build a little timer plug in that would help better keep track of time. I wanted it to do three things:</p>
<ol>
<li>Tell me how many minutes until the official start time of my presentation</li>
<li>Tell me how many minutes left until I was supposed to be done</li>
<li>Tell me when I was done, and how many minutes over I was</li>
</ol>
<h2 id="inserting-a-canvas-element">Inserting a canvas element <a class="direct-link" href="https://petelepage.com/blog/2011/09/html5-slide-decks-presentation-timer/#inserting-a-canvas-element">#</a></h2>
<p>Let’s jump right into the code! When the timer starts, it inserts a <code><canvas></code> element at the beginning of the <code><body></code> element with <a href="http://mzl.la/r5FuiJ">insertAdjacentHTML</a>. The canvas element is going to draw our clock face and display the number of minutes left.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">var</span> <span class="token function-variable function">insertElement</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">var</span> body <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementsByTagName</span><span class="token punctuation">(</span><span class="token string">"body"</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /> body<span class="token punctuation">.</span><span class="token function">insertAdjacentHTML</span><span class="token punctuation">(</span><span class="token string">"afterbegin"</span><span class="token punctuation">,</span><br /> <span class="token string">'<canvas id="cClock" width="30" height="30"></canvas>'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<h2 id="make-it-look-pretty%2C-and-subtle">Make it look pretty, and subtle <a class="direct-link" href="https://petelepage.com/blog/2011/09/html5-slide-decks-presentation-timer/#make-it-look-pretty%2C-and-subtle">#</a></h2>
<p>Since I wanted things to be very subtle and configurable, I used CSS to set the position to the upper left corner, and set the opacity to 0.25 to it mostly invisible so that I’ll likely be the only one who notices it. I also created a hover pseudo element so that I can hover over the element if I need to. Finally, I added a hidden class so that the element is completely invisible if the presentation ended a long time ago.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">#cClock</span> <span class="token punctuation">{</span><br /> <span class="token property">position</span><span class="token punctuation">:</span> fixed<span class="token punctuation">;</span><br /> <span class="token property">top</span><span class="token punctuation">:</span> 0px<span class="token punctuation">;</span><br /> <span class="token property">left</span><span class="token punctuation">:</span> 0px<span class="token punctuation">;</span><br /> <span class="token property">opacity</span><span class="token punctuation">:</span> 0.25<span class="token punctuation">;</span><br /> <span class="token property">z-index</span><span class="token punctuation">:</span> 1000<span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br /><span class="token selector">#cClock:hover</span> <span class="token punctuation">{</span> <span class="token property">opacity</span><span class="token punctuation">:</span> 1.0<span class="token punctuation">;</span> <span class="token punctuation">}</span><br /><span class="token selector">#cClock.hidden</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token punctuation">}</span><br /></code></pre>
<h2 id="refreshing-the-clock">Refreshing the clock <a class="direct-link" href="https://petelepage.com/blog/2011/09/html5-slide-decks-presentation-timer/#refreshing-the-clock">#</a></h2>
<p>When I start the clock, it uses a <code>setTimeout</code> to fire at a specific interval. I decided to use <code>setTimeout</code> instead of <code>setInterval</code> because I wanted to be able to dynamically change the amount of time between refreshes, depending on where in the presentation I was. If I was close to either the start time or end time, I want the clock to update more often, where as in the middle, it can fire every minute.</p>
<p>Drawing the clock</p>
<p>Getting the clock to draw in the way I wanted took a little bit of tinkering.</p>
<pre class="language-js"><code class="language-js"> <span class="token keyword">var</span> <span class="token function-variable function">drawClock</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">min<span class="token punctuation">,</span> color</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">var</span> canvas <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token operator">&</span>quot<span class="token punctuation">;</span>cClock<span class="token operator">&</span>quot<span class="token punctuation">;</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">var</span> ctx <span class="token operator">=</span> canvas<span class="token punctuation">.</span><span class="token function">getContext</span><span class="token punctuation">(</span><span class="token string">'2d'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> ctx<span class="token punctuation">.</span><span class="token function">clearRect</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">30</span><span class="token punctuation">,</span><span class="token number">30</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> ctx<span class="token punctuation">.</span>lineWidth <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">;</span><br /> ctx<span class="token punctuation">.</span>strokeStyle <span class="token operator">=</span> color<span class="token punctuation">;</span><br /> ctx<span class="token punctuation">.</span><span class="token function">beginPath</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> ctx<span class="token punctuation">.</span><span class="token function">arc</span><span class="token punctuation">(</span><span class="token number">15</span><span class="token punctuation">,</span> <span class="token number">15</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">,</span> <span class="token function">toRadians</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">90</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">toRadians</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span>min<span class="token operator">-</span><span class="token number">60</span><span class="token punctuation">)</span><span class="token operator">/</span><span class="token number">60</span><span class="token punctuation">)</span><span class="token operator">*</span><span class="token number">360</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token number">90</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> ctx<span class="token punctuation">.</span><span class="token function">stroke</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> ctx<span class="token punctuation">.</span>fillStyle <span class="token operator">=</span> color<span class="token punctuation">;</span><br /> ctx<span class="token punctuation">.</span>font<span class="token operator">=</span><span class="token string">"10px sans-serif"</span><span class="token punctuation">;</span><br /> ctx<span class="token punctuation">.</span>textAlign <span class="token operator">=</span> <span class="token string">"center"</span><span class="token punctuation">;</span><br /> ctx<span class="token punctuation">.</span>textBaseline <span class="token operator">=</span> <span class="token string">"middle"</span><span class="token punctuation">;</span><br /> ctx<span class="token punctuation">.</span><span class="token function">fillText</span><span class="token punctuation">(</span>min<span class="token punctuation">,</span> <span class="token number">15</span><span class="token punctuation">,</span> <span class="token number">15</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span></code></pre>
<h2 id="math-is-hard%2C-let%E2%80%99s-go-shopping!*">Math is hard, let’s go shopping!* <a class="direct-link" href="https://petelepage.com/blog/2011/09/html5-slide-decks-presentation-timer/#math-is-hard%2C-let%E2%80%99s-go-shopping!*">#</a></h2>
<p><code>drawClock(min, color)</code> grabs the <code><canvas></code> element, and uses clearRect() to erase everything in the box. I then set the line width and color and start the drawing the circle with <code>beginPath()</code> and <a href="http://mzl.la/nRiHGl">arc()</a>. Let’s quickly start at the end, the “true” tells arc to draw the circle counterclockwise. Then, the first three parameters tell the arc to put the center at 15, 15 with a radius of 10px. The next two parameters are the angles to start drawing the circle at in radians. If you remember high school algebra (which I don’t), 0 in radians on a circle would be equivalent of 3pm on a clock, so to figure out where the top is, we need to rotate -90 degrees and then convert to radians with toRadians(-90).</p>
<p>To calculate the second arc position, we need to convert everything to radians again, but we also need to figure out the angle in degrees. Since I wanted the circle to become more complete the closer the time gets to zero, I started by taking the minutes and subtracting 60. Next, I needed to figure out where (in degrees) the minutes would be, to do that, I divided by 60 to get a percentage and multiplied by 360 to get the degrees along the circle.</p>
<p>For example to figure out where the circle should end if the minutes were 15:</p>
<p>toRadians((((15 - 60) / 60) * 360) - 90)</p>
<p>-45 / 60 = -0.75 – The arc line should continue three quarters of the way around</p>
<p>-0.75 * 360 = -270 – The angle in degrees that the arc should draw to</p>
<p>-270 - 90 = -360 – The angle in radians, and remember my comment earlier that 0 (or 360) would be at the 3 o’clock position.</p>
<p>Phew! When coding this up, that was the hardest part for me. I’m not exactly the biggest math fan! 😃 Next I called <code>.stroke()</code> to draw the circle, and sure enough - the circle appears! Finally, I wanted to show the number of minutes left in the center of the circle. I did that by using <code>fillText()</code>. To get things to fit in the circle perfectly centered, I aligned everything middle and center, and with <code>fillText</code>, set the base point to 15, 15 - the center of the box.</p>
<h2 id="getting-the-countdown-timer-on-screen">Getting the countdown timer on screen <a class="direct-link" href="https://petelepage.com/blog/2011/09/html5-slide-decks-presentation-timer/#getting-the-countdown-timer-on-screen">#</a></h2>
<p>Finally - to create an instance of the clock in a presentation, I created a JSON config object that contained the start time, and a length and a warning time, and then created an instance of the timer.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text/javascript<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>js/timer.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text/javascript<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /> window<span class="token punctuation">.</span>timerConfig <span class="token operator">=</span> window<span class="token punctuation">.</span>timerConfig <span class="token operator">||</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">settings</span> <span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">date</span><span class="token operator">:</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token string">'Sept 19 2011 16:30'</span><span class="token punctuation">)</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">minutes</span><span class="token operator">:</span> <span class="token number">45</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">warnAt</span><span class="token operator">:</span> <span class="token number">10</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">;</span><br /> <span class="token keyword">var</span> t <span class="token operator">=</span> <span class="token function">PresentationTimer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> t<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /></code></pre>
<h2 id="in-action">In action <a class="direct-link" href="https://petelepage.com/blog/2011/09/html5-slide-decks-presentation-timer/#in-action">#</a></h2>
<p>I’ve iframed the timer into the window below, or you can try it <a href="http://bit.ly/pIUxqH">here</a>. It creates a new instance of the timer, and sets the start time to be two minutes from now, with a length of 2 minutes and a warning at 1 minute. I also made a small change to the CSS so that it’s less transparent and you can see it better.</p>
<iframe src="https://petelepage.com/scratch/presotimer/" style="border: none; width:50px; height: 50px;box-shadow:none;"></iframe>
<h2 id="summary">Summary <a class="direct-link" href="https://petelepage.com/blog/2011/09/html5-slide-decks-presentation-timer/#summary">#</a></h2>
<p>You can see the entire timer.js file in <a href="http://bit.ly/qwqDUc">GitHub</a>, or even grab the <a href="http://bit.ly/nifQCq">whole project.</a>. I really want to integrate this into the HTML5Slides template at some point, because I think it’s pretty useful. Feel free to grab it, use it, fork it, what ever works for you!</p>
<p>*Math is hard, let’s go shopping is one of my favorite Simpsons quotes.</p>
Google Developer Days - South America2011-09-20T00:00:00Zhttps://petelepage.com/blog/2011/09/google-developer-days-south-america/<p><a href="https://petelepage.com/assets/gdd_logo.jpg"><img src="https://petelepage.com/assets/gdd_logo.jpg" alt="Google Developer Day 2011" title="Google Developer Day 2011 Logo" /></a>My leg of the Google Developer Day tour is almost over, and it’s been AWESOME! Wow! Not only have I now visited 6 out of 7 continents (anyone know of any conferences or events in Antartica - it’s the only one left I’ve not been to), but I was able to add Sao Paulo and Buenos Aires to the list of cities that I need to come back and visit while on vacation. I’ve been thoroughly impressed with all of the developers here who showed me some of the really cool stuff that they’re working on.</p>
<p>I presented two sessions in at each of the Google Developer Days (videos are coming shortly and I’ll link to them as soon as they’re posted), and had a small part in the keynote demoing the Chrome stuff. Our venue in Buenos Aires, a Catholic University, was pretty neat, though as I joked, the last time I was in a university classroom, I was probably at the back of the room trying to stay awake. When we got to the venue on Sunday night to do our rehearsals and tech checks, they hadn’t put the Google Developer Day decorations up yet, which was a little weird because there was a crucifix on one side, and a large photo of the previous Pope on the other side. We also hit a little snag because they blocked a couple of the websites that we needed! Thankfully they were really good and got them unblocked pretty quickly.</p>
<p>I think my favorite on stage oops was during the live speech recognition demo. Any time you do a speech recognition demo on stage in a big venue, the acoustics work against you - but can sometimes lead to comical results. In Brazil, I tried to say “Hola Brazil”, which Chrome thought was “Fort Lauderdale”. I did the same demo in Buenos Aires, and got “Call Argentina”.</p>
<p>I’ve listed the demos and slide decks below, and as soon as the videos go online, I’ll get a post out about those! The slide decks between the two countries are the same, the only thing that changed was the title slide.</p>
<p>Keynote Demos</p>
<ul>
<li><span><a href="http://bit.ly/okUXoh">All Is Not Lost</a> - right click on the text box, and choose Inspect Element, then right click on the tag to Add an Attribute, and add the x-webkit-speech attribute. Then you can try your own speech demo!</span></li>
<li><span><a href="http://bit.ly/o1xIrV">Flux Sliders</a>, <a href="http://bit.ly/pFdzde">Movie Posters</a> & <a href="http://bit.ly/n0cFbB">jQuery Photo Gallery</a> - the CSS3 3D photo gallery demos</span></li>
<li><span><a href="http://bit.ly/rnkYmn">WebGL Water</a> - the beautiful pool demo with WebGL</span></li>
<li><span><a href="http://bit.ly/qwi9ka">SimCity 2000</a> - the Native Client demo</span></li>
</ul>
<p>Sao Paulo, Brazil</p>
<ul>
<li><span class="Apple-style-span" style="line-height: 18px;"><a href="http://bit.ly/pDIxsy">Bleeding Edge HTML5</a> (be sure to try in Canary or other super new browser)</span></li>
<li><span class="Apple-style-span" style="line-height: 18px;"><a href="http://bit.ly/qQxz1q">Building Modern Web Apps</a></span></li>
</ul>
<p>Buenos Aires, Argentina</p>
<ul>
<li><span class="Apple-style-span" style="line-height: 18px;"><a href="http://bit.ly/nFqST8">Bleeding Edge HTML</a> (be sure to try in Canary or other super new browser)</span></li>
<li><a href="http://bit.ly/nrjLs7">Building Modern Web Apps</a></li>
</ul>
<div><span class="Apple-style-span" style="line-height: 19px;">Thanks for a great trip guys - and if you're in Buenos Aires and want to grab a beer (or wine) and chat about web apps, I'd love to meet and see what you're working on! </span></div>
HTML5 Hack-A-Thon Coming To A City Near You2011-07-20T00:00:00Zhttps://petelepage.com/blog/2011/07/html5-hack-a-thon-coming-to-a-city-near-you/<p><img src="https://petelepage.com/assets/javascript.jpg" alt="JavaScript code on computer screen" />Well, hopefully it’ll be near you - if not, let me know in the comments where you are so we can try to get some more of these events set up!</p>
<p>Google is holding a few developer focused hack-a-thons around the US and would like to invite you to attend. At these events, we will be covering some of the latest and greatest features of HTML5, Chrome Web Store and In-App Payments. You will learn about the end to end flow of building a web app, distributing the app and monetizing it using these APIs. You will also have the opportunity to work hands on with the APIs and ask our experts (including me) questions. I’ll be helping out in Chicago and Mountain View.</p>
<p>For more information on the events and to register, please check out the location specific links below:</p>
<p>New York - 8/1 - <a href="http://bit.ly/nqeLGh">http://www.eventbrite.com/event/1892631913</a></p>
<p>Chicago - 8/3 - <a href="http://bit.ly/oS7xKh">http://www.eventbrite.com/event/1910102167</a></p>
<p>Seattle - 8/8 - <a href="http://bit.ly/pdc3LV">http://www.eventbrite.com/event/1910138275</a></p>
<p>Mountain View - 8/11 - <a href="http://bit.ly/nXuLtG">http://www.eventbrite.com/event/1910202467</a></p>
<ul>
<li>Image <a href="http://www.flickr.com/photos/dmitry-baranovskiy/2378867408/">http://www.flickr.com/photos/dmitry-baranovskiy/2378867408/</a></li>
</ul>
Is It Time To Rethink Web Navigation?2011-07-11T00:00:00Zhttps://petelepage.com/blog/2011/07/is-it-time-to-rethink-web-navigation/<p>While on my way into work this morning, I read a post on <a href="http://sixrevisions.com/">SixRevisions</a> asking the question “<a href="http://sixrevisions.com/user-interface/is-it-time-to-rethink-website-navigation/">Is It Time To Rethink Website Navigation?</a>” For a very long time, navigation on websites has been stagnant, it’s either a navbar across the top, or maybe down the side. Sometimes, with a little JavaScript, it’s interactive, but it almost always contains the same types of link. The logo almost always takes you “home”, there’s an about link, a site index link, contact us, well, you know what I’m talking about. Oh, and don’t forget about the standard footer at the bottom of most sites.</p>
<p><a href="https://petelepage.com/assets/greenhouses.png"><img src="https://petelepage.com/assets/greenhouses-300x200.png" alt="" title="greenhouses" /></a></p>
<p>We’ve certainly seen a number of web sites start to push the boundaries of traditional navigation and they call out a couple like <a href="http://www.clairetnet.com/serre-a-voeux/index.php?lang=en">The Wishes Greenhouse at Clair et Net</a> or how other sites use responsive web design to provide a better experience depending on the capabilities of the device you’re on.</p>
<h2 id="web-pages-%26-sites-vs.-web-applications">Web Pages & Sites vs. Web Applications <a class="direct-link" href="https://petelepage.com/blog/2011/07/is-it-time-to-rethink-web-navigation/#web-pages-%26-sites-vs.-web-applications">#</a></h2>
<p>This worked well when the web was only about sites and pages - navigating from one page to another and consuming content. But as we see more and more web applications come online, the traditional navigation model doesn’t work any more.</p>
<p>I think the author missed a great opportunity though to talk about the difference in navigation between web applications and web sites - two very different models. In a web site, navigation provides a connection from one page to another and a way to move around from one piece of content to another. Web application navigation is different, instead of moving between pages, it’s more about providing the user with an ability to control and interact with content. Changing the paradigm away from navigation towards control is one of the core components that separate websites and pages from applications.</p>
<p><a href="https://petelepage.com/assets/google_music1.jpg"><img src="https://petelepage.com/assets/google_music1.jpg" alt="" title="google_music1" /></a></p>
<p>Let’s take a quick look at <a href="http://music.google.com/">Music by Google</a>. Notice there isn’t a nav bar across the top, or on the sides, and there isn’t a footer either? Instead along the right side, there are a number of links that change my view. At the bottom, where the footer normally would be are the player controls (back, play, forward, volume, etc). Instead of providing links to help me get somewhere - the links on the page help me accomplish something, or change the view that I have (maybe to a different playlist, artist or genre).</p>
<h2 id="being-more-app-y">Being More App-y <a class="direct-link" href="https://petelepage.com/blog/2011/07/is-it-time-to-rethink-web-navigation/#being-more-app-y">#</a></h2>
<p>So what makes a web application app-y? Certainly the navigation and controls do. When I look at a web application and how the designers and developers have laid out the navigation, I ask myself how this app might be different if it were a client or mobile application. When was the last time you saw the privacy policy, terms of service or copyright notice not in an about screen on a desktop app, or tucked away under settings in a mobile application? Imagine if that were at the bottom of iTunes and took up the bottom 10%! And we see this all the time in web applications. In fact, GMail is one of my favorite offenders here. Why not tuck that away under settings in top right?</p>
<p><a href="https://petelepage.com/assets/gmail.png"><img src="https://petelepage.com/assets/gmail.png" alt="" title="gmail" /></a></p>
<h2 id="step-out-of-the-box">Step Out Of The Box <a class="direct-link" href="https://petelepage.com/blog/2011/07/is-it-time-to-rethink-web-navigation/#step-out-of-the-box">#</a></h2>
<p>It will take some time for this paradigm to change, but it’s an important one. Today we still think in the web site and web page “box” too often when creating web applications. <strong>Let’s get out of that box, and make web applications feel more like <em>applications</em> and less like web pages</strong>.</p>
What Makes For A Great Web App?2011-07-06T00:00:00Zhttps://petelepage.com/blog/2011/07/what-makes-for-a-great-web-app/<p>What makes a web application great? I recently sat down with Christos, one of the guys on my team to look at a number of the top web applications in the Chrome Web Store to try to identify which ones were really great, versus good, and what advice we could give to the good apps to become “great”.</p>
<p>Before we could start rating these apps, we built out a set of principles and criteria that we felt were important in providing a great application experience on the web. We based our principles on some <a href="http://code.google.com/chrome/apps/articles/thinking_in_web_apps.html">earlier work</a> done by <a href="http://twitter.com/#!/Mahemoff">Mike</a> and <a href="http://twitter.com/#!/Paul_Kinlan">Paul</a>, and refined their thinking into three principles. We tried to create a set of objective criteria for each of the principles that we could use to rate the apps - and while we didn’t actually assign a score, we wanted to be as objective as possible.</p>
<h2 id="a-tight-focus">A Tight Focus <a class="direct-link" href="https://petelepage.com/blog/2011/07/what-makes-for-a-great-web-app/#a-tight-focus">#</a></h2>
<p>A web application has a tight focus encouraging people to interact, engage, and accomplish something, rather than passively view content. It doesn’t distract people with content or components that are orthogonal to the task they’re trying to complete. People integrate it into their day to day lives because it feels like a natural extension of the things that they understand and do every day.</p>
<ul>
<li>The application is limited to a single, core scenario</li>
<li>Orthogonal scenarios and tasks are removed from view and not presented to the user</li>
<li>The primary application components are accessible at all times</li>
<li>Standard web page content and navigation is minimized and the application controls are the primary focus</li>
<li>The application looks and behaves consistently across different screens, dialogs, controls and other components</li>
<li>The application starts immediately and requires no set up or configuration before it’s first use</li>
<li>When starting the application, the user is taken directly to the application instead of a landing page or marketing page</li>
<li>The application can be used without registering or signing in, or the sign in/up process is extremely easy</li>
</ul>
<h2 id="rich-visual-experiences">Rich Visual Experiences <a class="direct-link" href="https://petelepage.com/blog/2011/07/what-makes-for-a-great-web-app/#rich-visual-experiences">#</a></h2>
<p>A web application provides rich visual experiences that will delight the eye without distracting the mind. A positive first impression makes users comfortable, assures them your application is reliable and professional and encourages people to make the product their own. A great web application puts a premium on aesthetics without sacrificing usability.</p>
<ul>
<li>Appropriate fonts and other typographic features are used to enhance readability</li>
<li>Graphics and illustrations are high quality and look professional</li>
<li>The application uses all of the screen real estate available to it</li>
<li>Textures, gradients, and shadows add to the visual appeal of the application</li>
<li>Controls and graphics convey meaning without requiring additional context</li>
<li>Controls and graphics offer a manner of realism</li>
<li>Standard web page content and navigation is minimized</li>
<li>Animations are used to introduce new content, dialogs, or other items that require attention or interaction from the user</li>
<li>Window resize events appropriately handle new screen sizes, resized graphics and images</li>
<li>Messages and information provided to the user is helpful, easy to understand and actionable</li>
</ul>
<h2 id="rich-interactive-experiences">Rich Interactive Experiences <a class="direct-link" href="https://petelepage.com/blog/2011/07/what-makes-for-a-great-web-app/#rich-interactive-experiences">#</a></h2>
<p>A web application provides a rich, interactive experience that makes technology transparent so people can complete their task confidently without ever having to know or understand what makes the application work.</p>
<ul>
<li>The application feels fast and responsive</li>
<li>The application provides appropriate notifications and information to keep the user informed</li>
<li>The application follows best practices for building fast web applications</li>
<li>The application provides a full featured offline experience</li>
<li>Input from the user is validated before being sent to the server and appropriate input types are used</li>
<li>Adding or removing data from the application is easy</li>
</ul>
<h2 id="a-couple-of-my-favorite-apps">A couple of my favorite apps <a class="direct-link" href="https://petelepage.com/blog/2011/07/what-makes-for-a-great-web-app/#a-couple-of-my-favorite-apps">#</a></h2>
<h3 id="quicknote">QuickNote <a class="direct-link" href="https://petelepage.com/blog/2011/07/what-makes-for-a-great-web-app/#quicknote">#</a></h3>
<p><a href="https://petelepage.com/assets/QuickNote.png"><img src="https://petelepage.com/assets/QuickNote-300x233.png" alt="" title="QuickNote" /></a><a href="https://chrome.google.com/webstore/detail/mijlebbfndhelmdpmllgcfadlkankhok">QuickNote</a> was on application that stood out in this category - not only does it focus on it’s primary task (note taking), it didn’t require any setup or logging in to get started.</p>
<h3 id="flixster">Flixster <a class="direct-link" href="https://petelepage.com/blog/2011/07/what-makes-for-a-great-web-app/#flixster">#</a></h3>
<p><a href="https://petelepage.com/assets/flixster.png"><img src="https://petelepage.com/assets/flixster-300x255.png" alt="" title="flixster" /></a>I’m a web developer who knows enough about design to appreciate good design, but not enough to do it myself. <a href="http://flixster.rottentomatoes.com/">Flixster</a> was a good example of involving design early on in the process - the use of rich CSS3 features like gradients, shadows and such make the app visually appealing and fun to use.</p>
<h3 id="280-slides">280 Slides <a class="direct-link" href="https://petelepage.com/blog/2011/07/what-makes-for-a-great-web-app/#280-slides">#</a></h3>
<p><a href="https://petelepage.com/assets/280Slides.png"><img src="https://petelepage.com/assets/280Slides-300x227.png" alt="" title="280Slides" /></a>I love it when I see a web application and think - it must be Flash, only to find out that it isn’t - seeing the creativity and ingenuity of other web developers is awesome. <a href="http://280slides.com/Editor/">280 Slides</a> is one of those web applications. Like PowerPoint or Keynote, it’s an app to build slide decks and presentations - but it’s all built using the open web stack. It was built using <a href="http://cappuccino.org/">Cappuccino</a>, a really powerful web framework for building apps, not web sites. Def worth checking out.</p>
The Anatomy of a Great Chrome Web Store Listing2011-06-14T00:00:00Zhttps://petelepage.com/blog/2011/06/the-anatomy-of-a-great-chrome-web-store-listing/<p>Your first chance to make a great impression with a new user is often on your application listing page in the <a href="https://chrome.google.com/webstore">Chrome Web Store</a>. People will make a split second decision whether to install your application based on what you put on there, so it’s important to make a great first impression. Let’s take a look at what makes a great Chrome Web Store application listing. This guide highlights a couple key components of a great Chrome Web Store listing. I tried not to repeat content from the documentation, but instead tease out the important parts or best practices. Be sure to follow the links for additional resources or details.</p>
<h2 id="tweetdeck-%26-google-maps">TweetDeck & Google Maps <a class="direct-link" href="https://petelepage.com/blog/2011/06/the-anatomy-of-a-great-chrome-web-store-listing/#tweetdeck-%26-google-maps">#</a></h2>
<p><a href="https://petelepage.com/assets/tweetdeck.png"><img src="https://petelepage.com/assets/tweetdeck-300x214.png" alt="" title="TweetDeck listing" /></a></p>
<p><a href="https://petelepage.com/assets/goog-maps.png"><img src="https://petelepage.com/assets/goog-maps-300x208.png" alt="" title="Google Maps listing" /></a></p>
<p>I think both <a href="https://chrome.google.com/webstore/detail/hbdpomandigafcibbmofojjchbcdagbl">TweetDeck</a> and <a href="https://chrome.google.com/webstore/detail/lneaknkopdijkpnocmklfnjbeapigfbh">Google Maps</a> do a great job of providing a compelling, and interesting listing, so we’ll use those as our primary examples.</p>
<h2 id="category">Category <a class="direct-link" href="https://petelepage.com/blog/2011/06/the-anatomy-of-a-great-chrome-web-store-listing/#category">#</a></h2>
<p><a href="https://petelepage.com/assets/category.png"><img src="https://petelepage.com/assets/category.png" alt="" title="TweetDeck Category Listing" /></a>Putting your application in to the most appropriate category will make it easy for users to find your application when they browse the store. You can choose up to two categories that are most appropriate for your application. You can find a <a href="http://code.google.com/chrome/webstore/docs/best_practices.html#categories">list</a> of the different categories as well as a description and examples for each in the <a href="http://code.google.com/chrome/webstore/docs/">Chrome Web Store developer documentation</a>.</p>
<p>Even though you can choose up to two categories, and your application will be listed under both, it will only show one category on the navigation breadcrumb when you view it in the Chrome Web Store. Unfortunately at this time there is no way to change which one is listed when you link directly to the page.</p>
<h2 id="header-details">Header Details <a class="direct-link" href="https://petelepage.com/blog/2011/06/the-anatomy-of-a-great-chrome-web-store-listing/#header-details">#</a></h2>
<h3 id="screen-shots-and-video">Screen Shots and Video <a class="direct-link" href="https://petelepage.com/blog/2011/06/the-anatomy-of-a-great-chrome-web-store-listing/#screen-shots-and-video">#</a></h3>
<p><a href="https://petelepage.com/assets/uploaded-screenshot.png"><img src="https://petelepage.com/assets/uploaded-screenshot-300x206.png" alt="" title="uploaded screenshot" /></a>Screen shots can be one of the most compelling factors in a user’s decision to install or buy an application, so it’s important to provide at least four or five screen shots that give a user a sense for how your application works, some of the features it offers and an idea of the different elements. You should always start with a beautiful screen shot that provides a detailed overview of the application experience first, and then drill into the additional elements. Make sure the screen shots show your application in use, so that users can see how the application works, and how they might use it. If you’re displaying any kind of data from the user, be sure to use safe or sample data in the screen shots instead of blurring things out or accidentally sharing personally identifiable information.</p>
<p>Screen shots should be full bleed images with square corners that are 400x275 pixels. Very few applications will actually fit within those dimensions, which means you may have to do some creative scaling, or cropping to provide appropriate screenshots. Be sure to not over-compress or scale images so that you can’t see any of the detail. Further details about supplying screen shots are available in the Chrome Web Store documentation under <a href="http://code.google.com/chrome/webstore/docs/images.html#screenshots">Supplying Images</a>.</p>
<p>A picture may be worth 1000 words, but video is worth many more. The Chrome Web Store allows you to add a YouTube video to the screen shot collection, allowing you to provide a richer way to show people your application, and convince them to install or buy it. Keep in mind that in many cases, people haven’t yet made the decision to install or buy your application, and likely won’t be willing to invest a lot of time, so optimize for a short, high quality overview that highlights the main features and will encourage people to try it.</p>
<h3 id="icon">Icon <a class="direct-link" href="https://petelepage.com/blog/2011/06/the-anatomy-of-a-great-chrome-web-store-listing/#icon">#</a></h3>
<p><a href="https://petelepage.com/assets/maps-icon.png"><img src="https://petelepage.com/assets/maps-icon.png" alt="" title="Google Maps application icon" /></a>Your application’s icon represents identity of your application, and is an important part of the overall brand. The icon should be professional, and appropriately sized so that it’s not pixelated or skewed. The icon file should be a 128x128 pixel PNG, where the actual icon size should be 96x96 (for square icons); an additional 16 pixels per side should be transparent padding, adding up to 128x128 total image size. Additional details about icons can be found in the <a href="http://code.google.com/chrome/webstore/docs/images.html#icons">Supplying Images</a> section of the Chrome Web Store documentation.</p>
<h3 id="verified-domain">Verified Domain <a class="direct-link" href="https://petelepage.com/blog/2011/06/the-anatomy-of-a-great-chrome-web-store-listing/#verified-domain">#</a></h3>
<p><a href="https://petelepage.com/assets/verified.png"><img src="https://petelepage.com/assets/verified.png" alt="" title="verified" /></a>Verifying your domain via the <a href="http://www.google.com/webmasters/">Google Webmaster Tools</a> is like adding a certificate of authenticity to something that you’re going to buy in the store. A verified application tells potential users they’re about the install a genuine, user experience from your site - and that they’re not getting a cheap, or potentially unsafe knock-off. Also, be sure the domain name that you’ve verified and are using makes sense, and isn’t a development or staging domain; which may scare away your users.</p>
<h3 id="short-description">Short Description <a class="direct-link" href="https://petelepage.com/blog/2011/06/the-anatomy-of-a-great-chrome-web-store-listing/#short-description">#</a></h3>
<p>The application manifest file (<a href="http://code.google.com/chrome/extensions/manifest.html">manifest.json</a>) has a property for a short description of your application. Use this to tell users about what your application does and why they should get excited and care about it. It’s limited to 132 characters, and cannot include any HTML. For example, the description for Angry Birds is simply “Birds! Slingshots! Destruction! Feathers! Fun!” For Gmail, it’s “Fast, searchable email with less spam.”</p>
<h3 id="customized-header-background">Customized Header Background <a class="direct-link" href="https://petelepage.com/blog/2011/06/the-anatomy-of-a-great-chrome-web-store-listing/#customized-header-background">#</a></h3>
<p><a href="https://petelepage.com/assets/header-background-image.png"><img src="https://petelepage.com/assets/header-background-image-150x150.png" alt="" title="Sample header background image" /></a>You can supply a customized background for the header description that appears on the right side of the header in your application’s store listing. The header background allows you to enhance the identity and branding elements on your listing page. When supplying the background image, make sure your app’s title and description are still readable and that the background elements don’t distract from the overall listing.</p>
<p>The header background image should fill the entire 570x275 pixel canvas, though don’t put anything important in the bottom 60px as those are reserved for the rating, buy or install buttons. Be sure to choose a background color that will provide the appropriate contrast with either white, or black text, though a dark or medium background color is recommended, like in this <a href="https://petelepage.com/assets/header-in-store-screenshot.png">example</a>. Additional details can be found in the <a href="http://code.google.com/chrome/webstore/docs/images.html#bg">Header background</a> section of the Chrome Web Store documentation.</p>
<h2 id="full-description">Full Description <a class="direct-link" href="https://petelepage.com/blog/2011/06/the-anatomy-of-a-great-chrome-web-store-listing/#full-description">#</a></h2>
<p>Beyond the concise description set in the manifest file, a detailed description can be provided via the Chrome Web Store Developer Dashboard. This is your space to really excite your potential users about your application and convince them to install or buy it. If you’ve already built a landing page on your web site, you can likely leverage what you’ve already written here.</p>
<p>You should provide a detailed description of your application, including it’s features, quotes from great reviews, information about recent updates, and any other information that might be relevant to potential users. If your application has a freemium or free trial, be sure to tell users the differences between the paid and free experience so they’re not surprised or disappointed by functionality that they might not have access to.</p>
<p>Remember, this description will be searchable, so be sure to use any key words in your description that might help users to find your application if they try searching for it without knowing the name of it.</p>
<p>If your product is compatible with a Google product, make reference to that Google product by using the text “for”, “for use with”, or “compatible with”, and be sure to include the ™ symbol with the Google trademark. Example: “for Google Chrome™”. For other third party products, be sure to check any requirements that they may have when describing your application.</p>
<h2 id="other-helpful-components">Other Helpful Components <a class="direct-link" href="https://petelepage.com/blog/2011/06/the-anatomy-of-a-great-chrome-web-store-listing/#other-helpful-components">#</a></h2>
<h3 id="version-number-%26-last-updated">Version Number & Last Updated <a class="direct-link" href="https://petelepage.com/blog/2011/06/the-anatomy-of-a-great-chrome-web-store-listing/#version-number-%26-last-updated">#</a></h3>
<p><a href="https://petelepage.com/assets/last-updated.png"><img src="https://petelepage.com/assets/last-updated.png" alt="" title="Last Updated Field" /></a>One of the great features of web applications is the ease at which we can add new features, fix bugs and enhance the overall user experience. It’s strongly recommended that you increment the version number in your manifest file and re-upload it to the Chrome Web Store any time you provide significant updates or bug fixes to your application. This ensures that the version number and last updated field on your application listing stay up to date, and users know that you’re application is fresh, and that you’re constantly working to make it better.</p>
<p>Also, when choosing a version number, it’s a good idea to start at 1.0, or as close to it as you can so that people know your application is ready for real world use and that you’re not something that’s still in beta or may not work as expected. If you’re not ready to be out of beta yet, that’s okay, but provide a realistic version number that indicates how close you are to being out of beta. And be sure to stay away from version numbers like 0.0.0.1.</p>
<h3 id="promotional-images">Promotional Images <a class="direct-link" href="https://petelepage.com/blog/2011/06/the-anatomy-of-a-great-chrome-web-store-listing/#promotional-images">#</a></h3>
<p><a href="https://petelepage.com/assets/small-promo-image.png"><img src="https://petelepage.com/assets/small-promo-image.png" alt="" title="Small Promotional Image" /></a>It’s always a good idea to provide a <a href="https://petelepage.com/assets/small-promo-image.png">small</a> and <a href="https://petelepage.com/assets/large-promo-image.png">large</a> promotional images; in case your application is chosen to be featured in one of the many featured application listings. If it doesn’t have at least one promotional image, it there’s little chance of it being chosen as a feature application! Both images should use the same branding and identity as the rest of the images that you supplied, and have a dark or medium background with light text. The small image should be 210x140 pixels and the large 585x220 pixels, both with square corners and no padding. Additional details on <a href="http://code.google.com/chrome/webstore/docs/images.html#promo">promotional images</a> can be found in the Chrome Web Store documentation.</p>
<h3 id="using-the-chrome-web-store-badge">Using the Chrome Web Store badge <a class="direct-link" href="https://petelepage.com/blog/2011/06/the-anatomy-of-a-great-chrome-web-store-listing/#using-the-chrome-web-store-badge">#</a></h3>
<p><a href="https://petelepage.com/assets/ChromeWebStore_Badge_v2_206x58.png"><img src="https://petelepage.com/assets/ChromeWebStore_Badge_v2_206x58.png" alt="" title="ChromeWebStore_Badge_v2_206x58" /></a>On your application home page, you can let others know that your app is available in the Chrome Web Store by using the “<a href="http://code.google.com/chrome/webstore/branding.html#badge">Available in the Chrome Web Store</a>” badge on your site and even going so far as to do browser sniffing to provide Chrome users with additional messaging. Be sure to check out</p>
<h2 id="conclusion">Conclusion <a class="direct-link" href="https://petelepage.com/blog/2011/06/the-anatomy-of-a-great-chrome-web-store-listing/#conclusion">#</a></h2>
<p>There’s a lot you can do to make sure that you make a great first impression with your Chrome Web Store listing that encourages users to buy or install your application. Good luck, and let’s go build some cool web apps!</p>
<p><strong>[Update]</strong> - thanks to <a href="http://twitter.com/mikewest">@mikewest</a> for pointing out a few grammatical and other mistakes!</p>
<p><strong>[Update2]</strong> - <a href="http://twitter.com/sethladd">@sethladd</a> reminded me that he’s got a great blog post titled <a href="http://blog.sethladd.com/2010/11/12-tips-for-great-chrome-web-store.html">13 Tips for a Great Chrome Web Store Listing Page</a></p>
TweeterFall - Web Workers2011-05-20T00:00:00Zhttps://petelepage.com/blog/2011/05/tweeterfall-web-workers/<p><img src="https://petelepage.com/assets/unresponsive-300x154.png" alt="" title="unresponsive" /></p>
<p>How often have you seen this dialog, or something similar - telling you that some scripts on a page are taking too long while your browser has become completely unresponsive. Because JavaScript runs in the same thread as the rest of the browser UI, we’re often faced with the challenge of being unable to run any complex or long running JavaScript without the fear of blocking the browser. That’s where web workers come in.</p>
<p>Web Workers are part of the HTML5 specification and allow you to run scripts in the background, independently of the main browser thread. This means that you can do complex calculations or run scripts that may take a while to run without yielding to keep the page responsive. The <a href="http://dev.w3.org/html5/workers/">W3C spec</a> has some great examples of use cases and other helpful descriptions that are worth reading for deeper details. It also notes that workers are relatively heavy weight and are not intended to be used in large numbers.</p>
<p>In order to to maintain thread safety, workers run in an effective sandbox, which means they don’t have access to the DOM or many other components. To get information back and forth between the worker and the main application, we need to use the postMessage method and listen for message events.</p>
<p>In TweeterFall, we used a worker thread poll Twitter for new tweets and then we ‘pushed’ those new tweets to the main thread. Let’s have a look how we used workers.</p>
<h2 id="starting-%26-stopping-the-worker">Starting & Stopping the Worker <a class="direct-link" href="https://petelepage.com/blog/2011/05/tweeterfall-web-workers/#starting-%26-stopping-the-worker">#</a></h2>
<p>We only ever have one working running in our application, and to maintain an easy handle to it, we defined it’s variable in the global scope. We then had two methods that would handle how the worker would behave - <code>startWorker()</code> and <code>stopWorker()</code>. The <code>startWorker()</code> method takes a parameter that provides information about the username or list that we want to pull tweets from, how often we should poll Twitter and the ID of the last tweet we saw.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">var</span> myWorker<span class="token punctuation">;</span><br /><span class="token keyword">function</span> <span class="token function">startWorker</span><span class="token punctuation">(</span><span class="token parameter">settings</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> settings<span class="token punctuation">.</span>cmd <span class="token operator">=</span> <span class="token string">"start"</span><span class="token punctuation">;</span><br /> settings<span class="token punctuation">.</span>lastTweetID <span class="token operator">=</span> <span class="token function">lastTweetID</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"WebWorker: Starting"</span><span class="token punctuation">,</span> settings<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> myWorker <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Worker</span><span class="token punctuation">(</span><span class="token string">"scripts/worker.js"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> myWorker<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"message"</span><span class="token punctuation">,</span> newTweetHandler<span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> myWorker<span class="token punctuation">.</span><span class="token function">postMessage</span><span class="token punctuation">(</span>settings<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br /><span class="token keyword">function</span> <span class="token function">stopWorker</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>myWorker <span class="token operator">!=</span> <span class="token keyword">undefined</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"WebWorker: Terminating"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> myWorker<span class="token punctuation">.</span><span class="token function">terminate</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> myWorker <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<p>In our <code>startWorker()</code> thread, we create the new worker with <code>myWorker = new Worker("scripts/worker.js");</code>, and then add an event listener and listen for any messages that the worker wants to pass back to us - this is the only way we’re going to be able to communicate to the worker because it’s working off in it’s own little isolated world. At this point, the worker exists, and if it wants to tell us anything, we’re ready to listen, but now we need to tell it to do something, which is the <code>myWorker.postMessage(settings);</code>.</p>
<p>Stopping our worker is pretty similar, we check to make sure that we have a valid object, and as long as we do, <code>.terminate()</code> will immediately close the worker without giving it an opportunity to shut down properly or clean up after itself. This works in our case because we don’t have anything in memory and we’re not doing any calculations of any kind. If you may need to save state or want to clean up after yourself, the better way to shut down would be to send a stop message via <code>.postMessage()</code> and within your worker thread, shut down gracefully by calling <code>self.close();</code> after completing any clean up.</p>
<h2 id="the-worker-thread">The Worker Thread <a class="direct-link" href="https://petelepage.com/blog/2011/05/tweeterfall-web-workers/#the-worker-thread">#</a></h2>
<p>Inside our worker thread, we need to set up an event listener to listen for messages from our parent thread and any other code that we want to execute.</p>
<pre class="language-js"><code class="language-js">self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'message'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">var</span> data <span class="token operator">=</span> e<span class="token punctuation">.</span>data<span class="token punctuation">;</span><br /> <span class="token keyword">switch</span> <span class="token punctuation">(</span>data<span class="token punctuation">.</span>cmd<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">case</span> <span class="token string">'start'</span><span class="token operator">:</span><br /> self<span class="token punctuation">.</span><span class="token function">postMessage</span><span class="token punctuation">(</span><span class="token string">"Worker: Starting"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token function">setConfig</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token function">readTweets</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">break</span><span class="token punctuation">;</span><br /> <span class="token keyword">case</span> <span class="token string">'stop'</span><span class="token operator">:</span><br /> self<span class="token punctuation">.</span><span class="token function">postMessage</span><span class="token punctuation">(</span><span class="token string">"Worker: Stopping"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> self<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">break</span><span class="token punctuation">;</span><br /> <span class="token keyword">default</span><span class="token operator">:</span><br /> self<span class="token punctuation">.</span><span class="token function">postMessage</span><span class="token punctuation">(</span><span class="token string">"Worker: Error - Unknown Command"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Our event listener listens for messages from the parent and will start or stop the worker as necessary. When we get the start command, we parse the settings data (<code>setConfig</code>), and then start reading tweets (<code>readTweets()</code>). For sake of readability, I’ll skip the code we used for <code>setConfig()</code>, but it’s easy enough to find if you want it.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span> <span class="token function">getURL</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">var</span> sinceID <span class="token operator">=</span> <span class="token string">""</span><span class="token punctuation">;</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">parseInt</span><span class="token punctuation">(</span>lastKnownTweetID<span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> sinceID <span class="token operator">=</span> <span class="token string">"&since_id="</span> <span class="token operator">+</span> lastKnownTweetID<span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> userList <span class="token operator">===</span> <span class="token string">'undefined'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token string">'http://twitter.com/statuses/user_timeline/'</span> <span class="token operator">+</span> twitterUser<br /> <span class="token operator">+</span> <span class="token string">'.json?count='</span> <span class="token operator">+</span> tweetCount<span class="token operator">+</span> <span class="token string">'&callback=processTweets'</span> <span class="token operator">+</span> sinceID<span class="token punctuation">;</span><br /> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br /> <span class="token comment">//For a list</span><br /> <span class="token keyword">return</span> <span class="token string">'http://api.twitter.com/1/'</span> <span class="token operator">+</span> twitterUser <span class="token operator">+</span> <span class="token string">'/lists/'</span> <span class="token operator">+</span><br /> userList <span class="token operator">+</span> <span class="token string">'/statuses.json?callback=processTweets'</span> <span class="token operator">+</span> sinceID<span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><br /><br /><span class="token keyword">function</span> <span class="token function">readTweets</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">try</span> <span class="token punctuation">{</span><br /> <span class="token keyword">var</span> url <span class="token operator">=</span> <span class="token function">getURL</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> self<span class="token punctuation">.</span><span class="token function">postMessage</span><span class="token punctuation">(</span><span class="token string">"Worker: Attempting To Read Tweets From - "</span> <span class="token operator">+</span> url<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token function">importScripts</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token keyword">catch</span> <span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> self<span class="token punctuation">.</span><span class="token function">postMessage</span><span class="token punctuation">(</span><span class="token string">"Worker: Error - "</span> <span class="token operator">+</span> e<span class="token punctuation">.</span>message<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token function">setTimeout</span><span class="token punctuation">(</span>readTweets<span class="token punctuation">,</span> <span class="token number">120000</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<p><code>readTweets()</code> is the function that does the dirty work, making a JSONP call to Twitter that requests the latest tweets. We encapsulated the URL generation into it’s own method so that we could use it even if we weren’t using workers. Because we don’t have access to the DOM, we can’t just insert a <code>&lt;script&gt;</code> block, so after getting the URL, we call <code>importScripts(url);</code>, which makes an synchronous call and injects the result into the worker. If we run into any problems, like over capacity, not being able to reach Twitter or anything else, it’s caught in the catch block, and we wait two minutes before trying again. Here’s the JSONP call we make <a href="http://api.twitter.com/1/petele/lists/chromedevrel-11/statuses.json?callBack=processTweets">http://api.twitter.com/1/petele/lists/chromedevrel-11/statuses.json?callBack=processTweets</a>.</p>
<p>The request we sent to Twitter includes <code>?callback=processTweets</code> in the query string which will cause <code>processTweets()</code> to be executed once we’ve pulled the new tweets from Twitter.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span> <span class="token function">processTweets</span><span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">var</span> len <span class="token operator">=</span> data<span class="token punctuation">.</span>length<span class="token punctuation">;</span><br /> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&</span>lt<span class="token punctuation">;</span> len<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>data<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>id_str <span class="token operator">>=</span> lastKnownTweetID<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> lastKnownTweetID <span class="token operator">=</span> data<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>id_str<span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /> <span class="token function">returnTweets</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br /><span class="token keyword">function</span> <span class="token function">returnTweets</span><span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>data<span class="token punctuation">.</span>length <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> self<span class="token punctuation">.</span><span class="token function">postMessage</span><span class="token punctuation">(</span><span class="token string">"Worker: New Tweets - "</span> <span class="token operator">+</span> data<span class="token punctuation">.</span>length<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> self<span class="token punctuation">.</span><span class="token function">postMessage</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br /> self<span class="token punctuation">.</span><span class="token function">postMessage</span><span class="token punctuation">(</span><span class="token string">"Worker: New Tweets - 0"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token function">setTimeout</span><span class="token punctuation">(</span>readTweets<span class="token punctuation">,</span> updateDelay<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<p><code>processTweets()</code> enumerates all of the tweets we got back to find the most recent tweet ID. That way, when we ask Twitter again later for tweets, we’re only asking for the ones that we haven’t seen. Once we’ve done that, we call <code>returnTweets()</code> to push the new tweets back to the application. We could have merged <code>processTweets()</code> and <code>returnTweets()</code>, and I don’t remember why I decided to do it as two separate methods. Once we’re returned the tweets via <code>returnTweets()</code> we use <code>setTimeout()</code> to call <code>readTweets()</code> again.</p>
<h2 id="a-couple-of-quick-tips">A couple of quick tips <a class="direct-link" href="https://petelepage.com/blog/2011/05/tweeterfall-web-workers/#a-couple-of-quick-tips">#</a></h2>
<p>A couple things to remember about web workers, no DOM access means that you can’t manipulate the DOM, so that means that if you want to create new UI components in threads, you can, but you need to do it in the thread, and then pass that back to the main application via <code>postMessage()</code>. Unfortunately, it also means that jQuery doesn’t work in threads, so generating those UI components becomes a little more complex. I’ve heard rumors of a worker safe jQuery library, but haven’t seen it yet. Hopefully! 😃</p>
<p>The other good thing to know is that you can debug works in the Chrome Developer Tools by clicking on the Scripts tab, and scrolling down in the column on the right to Workers and clicking on the debug checkbox.</p>
<h2 id="summary">Summary <a class="direct-link" href="https://petelepage.com/blog/2011/05/tweeterfall-web-workers/#summary">#</a></h2>
<p>I found using workers to be really easy, and a good way of handling this. There are plenty of other good ways to use workers - and it gives us some pretty good power to be able to push complex work down to the client. Especially as browsers JavaScript engines are getting faster and faster.</p>
<h3 id="additional-resources">Additional Resources <a class="direct-link" href="https://petelepage.com/blog/2011/05/tweeterfall-web-workers/#additional-resources">#</a></h3>
<ul>
<li><a href="https://developer.mozilla.org/en/using_web_workers">Mozilla Developer Center - Using Web Workers</a></li>
<li><a href="http://dev.w3.org/html5/workers/">W3C Web Workers Spec</a></li>
<li><a href="http://en.wikipedia.org/wiki/Web_Workers">Web Workers on Wikipedia</a></li>
<li><a href="http://www.html5rocks.com/tutorials/workers/basics/">HTML5Rocks Web Workers Tutorial</a></li>
</ul>
TweeterFall - Notifications2011-05-19T00:00:00Zhttps://petelepage.com/blog/2011/05/tweeterfall-notifications/<p><img src="https://petelepage.com/assets/notification.png" alt="" title="notification" /></p>
<p>In <a href="http://bit.ly/TweeterFall">TweeterFall</a>, when a new tweet is received, we notify the user about that tweet via desktop notifications. If you use Chrome and Gmail, you’ve probably seen notifications pop up when you receive a new mail, or about an upcoming appointment. The Desktop Notifications API allows script to request a small toast to appear on the desktop of the user. The contents of the toast can either be specified as iconUrl/title/text strings, or as a URL to a remote resource which contains the contents of the toast. They’re part of the <a href="http://dev.w3.org/2006/webapi/WebNotifications/publish/">WebAPI spec</a>, though still in early draft, and so far only implemented in Chrome.</p>
<p>Since notifications are still something that may change, in Chrome, they’re under the <code>webkit</code> vendor prefix. The Chromium implementation details can be found on the <a href="http://www.chromium.org/developers/design-documents/desktop-notifications/api-specification">Chromium</a> site.</p>
<p>There are two types of notifications that you can surface, what I’ll call simple notifications like the one on the left that contains an image, a headline and some text; and HTML notifications. HTML notifications are much more powerful because instead of sending the image, headline and text, you send it a URL, and it will display the contents of that URL. That means you can style your notification to match your applications UI and even provide interactivity through links, javascript, etc.</p>
<p>Let’s have a look at how we implemented new tweet notifications in TweeterFall.</p>
<h2 id="requesting-permission">Requesting Permission <a class="direct-link" href="https://petelepage.com/blog/2011/05/tweeterfall-notifications/#requesting-permission">#</a></h2>
<p>When the user turns on notifications in TweeterFall, we need to check to see if we have permission to show notifications and if we don’t we need to ask for it. Desktop Notifications pop open a new window and could quickly become an easy spam target if we didn’t provide some kind of permission system. When a user grants or denies permission, they do so for the domain.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span> <span class="token function">requestPermissionIfRequired</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">hasNotificationPermission</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token punctuation">(</span>window<span class="token punctuation">.</span>webkitNotifications<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> window<span class="token punctuation">.</span>webkitNotifications<span class="token punctuation">.</span><span class="token function">requestPermission</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<p>In <code>requestPermissionIfRequired()</code>, we check to see if the browser supports notifications, and if we already have permission. If we don’t have permission, but notifications are supported, Chrome prompts the user asking for permission to provide notifications.</p>
<p><img src="https://petelepage.com/assets/The-notifications-permission-infobar-in-Google-Chrome.png" alt="" title="The notifications permission infobar in Google Chrome" /></p>
<h2 id="checking-for-permission">Checking For Permission <a class="direct-link" href="https://petelepage.com/blog/2011/05/tweeterfall-notifications/#checking-for-permission">#</a></h2>
<p>I also use feature detection here to check to see if notifications are supported in the browser. We will only return true if notifications are supported and the user has already given permission to show notifications.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span> <span class="token function">hasNotificationPermission</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token operator">!</span><span class="token operator">!</span><span class="token punctuation">(</span>window<span class="token punctuation">.</span>webkitNotifications<span class="token punctuation">)</span><br /> <span class="token operator">&&</span> <span class="token punctuation">(</span>window<span class="token punctuation">.</span>webkitNotifications<span class="token punctuation">.</span><span class="token function">checkPermission</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<h2 id="show-the-notification">Show the Notification <a class="direct-link" href="https://petelepage.com/blog/2011/05/tweeterfall-notifications/#show-the-notification">#</a></h2>
<p>We’re finally ready to show our notification to the user.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span> <span class="token function">showNotification</span><span class="token punctuation">(</span><span class="token parameter">pic<span class="token punctuation">,</span> title<span class="token punctuation">,</span> text</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">hasNotificationPermission</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">var</span> notificationWindow <span class="token operator">=</span><br /> window<span class="token punctuation">.</span>webkitNotifications<span class="token punctuation">.</span><span class="token function">createNotification</span><span class="token punctuation">(</span>pic<span class="token punctuation">,</span> title<span class="token punctuation">,</span> text<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> notificationWindow<span class="token punctuation">.</span><span class="token function">show</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">notWin</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> notWin<span class="token punctuation">.</span><span class="token function">cancel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">10000</span><span class="token punctuation">,</span> notificationWindow<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"showNotification: No Permission"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<p>The method takes three parameters, the image to use, the title and the text do display. When the function is called, it checks to see if it has permission using our previously established permission check that also encompasses feature detection, so we know we’ll be safe calling this even if the browser doesn’t support notifications. We then create the notification with <code>window.webkitNotifications.createNotification(pic, title, text)</code> and show it with <code>.show()</code>.</p>
<p>Immediately below that, we have a <code>setTimeout</code> call with an anonymous function that takes one parameter (the window handle to the notification) and will hide the notification after the timeout period has expired. If we didn’t do this, the notification would remain on screen until the user clicked the close button, or closed the browser. I’ve hard coded the timeout to be 10000ms, and you can change this to suit your needs.</p>
<h2 id="summary">Summary <a class="direct-link" href="https://petelepage.com/blog/2011/05/tweeterfall-notifications/#summary">#</a></h2>
<p>As you can see, showing notifications is pretty easy, check & ask for permission, and then show the notification. You can use these to let the user know when a task is complete, when there’s new information that they might be interested in and so forth. Obviously these only work when a user is on your site, but you can overcome that limitation by publishing your app in the Chrome Web Store and using background pages.</p>
<h2 id="other-resources">Other Resources <a class="direct-link" href="https://petelepage.com/blog/2011/05/tweeterfall-notifications/#other-resources">#</a></h2>
<p>Be sure to check out the <a href="http://www.html5rocks.com/tutorials/notifications/quick/">notification tutorial</a> on <a href="http://html5rocks.com/">HTML5Rocks</a>, it covers some additional things like the events that notifications fire and using HTML notifications.</p>
Building TweeterFall2011-05-19T00:00:00Zhttps://petelepage.com/blog/2011/05/building-tweeterfall/<p><a href="https://petelepage.com/assets/TweeterFall.png"><img src="https://petelepage.com/assets/TweeterFall-300x181.png" alt="" title="TweeterFall" /></a></p>
<p>I want to devote some time to how <a href="http://twitter.com/edr">Ernest</a> and I built <a href="http://bit.ly/TweeterFall">TweeterFall</a>, the demo that we used for our <a href="http://www.io-bootcamp.com/">I/O Boot Camp</a> presentation last week. Instead of showing isolated demos of some of the new HTML5 features, we wanted to show how you might use some of these things in real world scenarios, and thus we built TweeterFall. TweeterFall is a Twitter visualizer that grabs the tweets from a list and shows them in a kind of waterfall like visualization.</p>
<p>What did we use to build TweeterFall?</p>
<h2 id="html5-semantics">HTML5 Semantics <a class="direct-link" href="https://petelepage.com/blog/2011/05/building-tweeterfall/#html5-semantics">#</a></h2>
<p>We used the new HTML5 semantic elements as our primary markup elements. The bar across the top is a <code><header></code>, the area on the left where the tweets fall is a <code><section></code>, the list of Tweets on the right and the configuration panel are both <code><aside></code>s, and the individual tweets are <code><article></code>s.</p>
<h2 id="geolocation">GeoLocation <a class="direct-link" href="https://petelepage.com/blog/2011/05/building-tweeterfall/#geolocation">#</a></h2>
<p>Since we figured that most people would be in SF, it’d be fun to use some kind of geo location to show how close the person was to your current position when they made their Tweet. To do that, we pull the users current location using navigator.geolocation.watchPosition and compare that to the geo data in the tweet. We used the <a href="http://www.html5rocks.com/tutorials/geolocation/trip_meter/">GeoLocation tutorial</a> from <a href="http://html5rocks.com/">HTML5Rocks.com</a> as our inspiration for this.</p>
<h2 id="indexeddb">IndexedDB <a class="direct-link" href="https://petelepage.com/blog/2011/05/building-tweeterfall/#indexeddb">#</a></h2>
<p>Twitter only allows you to pull about 20 tweets at a time, so if you want to see anything before that, you either need to log in (and we didn’t really want to deal with OAuth for this presentation) or store the previous tweets locally. IndexedDB was the perfect solution for this, as it allows you to store significant quantities of structured data. Figuring out IndexedDB was probably my biggest challenge for this project, mostly because it’s pretty new and there isn’t a lot of documentation on it yet. That and the fact that everything you do is asynchronous, so you need lots of fun callbacks! Look for a whole post on this one shortly.</p>
<h2 id="localstorage">LocalStorage <a class="direct-link" href="https://petelepage.com/blog/2011/05/building-tweeterfall/#localstorage">#</a></h2>
<p>IndexedDB works great for structured data, but we also wanted a place to store simple configuration data, so we used web storage for that. I wanted to have my configuration data in an object so I could easily access things, so I used JSON to parse and stringify my config object as it went into and came out of local storage.</p>
<h2 id="notifications">Notifications <a class="direct-link" href="https://petelepage.com/blog/2011/05/building-tweeterfall/#notifications">#</a></h2>
<p>We used simple notifications to alert users when there were new tweets, and also included a special handler so that they would disappear after a couple of seconds and not remain on screen for too long. One of the updates I want to make here is to use HTML notifications, so that we can make things look a little more like the rest of the UI.</p>
<h2 id="speech-input">Speech Input <a class="direct-link" href="https://petelepage.com/blog/2011/05/building-tweeterfall/#speech-input">#</a></h2>
<p>Okay, so this one was a bit gratuitous. 😃 If you add x-webkit-speech to text inputs, Chrome will add a microphone icon to the text box. When the user clicks on the icon, Chrome turns on the microphone, listens for input and then converts what you said into text! No JavaScript, just all magic!</p>
<h2 id="web-workers">Web Workers <a class="direct-link" href="https://petelepage.com/blog/2011/05/building-tweeterfall/#web-workers">#</a></h2>
<p>Web Workers are great for handing off complex or intensive processes off to another thread so they don’t block the rest of the browser. In our case, we wanted to isolate the requests we were making to Twitter into their own place and allow them to “push” notifications to us. One thing to remember with Web Workers, is that they run off in their own separate thread, and don’t have access to the DOM or most other window objects (which means jQuery won’t work either).</p>
<h2 id="canvas">Canvas <a class="direct-link" href="https://petelepage.com/blog/2011/05/building-tweeterfall/#canvas">#</a></h2>
<p>This was a bit gratuitous too, we’ve got a fun update in mind for this that is equally gratuitous, but much prettier.</p>
<h2 id="2d-transforms">2D Transforms <a class="direct-link" href="https://petelepage.com/blog/2011/05/building-tweeterfall/#2d-transforms">#</a></h2>
<p>When you mouse over the list of Tweets on the right, they get bigger to make them easier to read. To do this, we use the :hover pseudo selector and apply a scale transform to make it bigger, and then a translation to ensure it appears in the right place. If we didn’t do the translation, part of the tweet would get cut off on the right side.</p>
<h2 id="css-animations">CSS Animations <a class="direct-link" href="https://petelepage.com/blog/2011/05/building-tweeterfall/#css-animations">#</a></h2>
<p>When I first created the TweeterFall visualization, I was using CSS transitions and transforms to animate the tweet as it fell down, but then realized that the better way to do it would be CSS Animations and transforms.</p>
<h2 id="web-fonts">Web Fonts <a class="direct-link" href="https://petelepage.com/blog/2011/05/building-tweeterfall/#web-fonts">#</a></h2>
<p>We used Web Fonts to spice up the look of the page somewhat, there are lots of great fonts available. Typically I’ll either grab fonts from <a href="http://www.fontsquirrel.com/">Font Squirrel</a> or <a href="http://www.google.com/webfonts">Google’s Web Fonts</a>. As Ernest presented the section on web fonts, he said something to the effect of developers have had only 8 fonts they can count on across multiple platforms/browsers, and because Comic Sans doesn’t really count, they only really had 7.</p>
<h2 id="chrome-developer-tools">Chrome Developer Tools <a class="direct-link" href="https://petelepage.com/blog/2011/05/building-tweeterfall/#chrome-developer-tools">#</a></h2>
<p>So we didn’t really use this in the app, but let me say this, the JavaScript console was the biggest life saver as we were debugging and trying to understand everything that was going on. Not only did we use a few console.log’s, we also were able to execute code and do all kinds of other great things. In fact, Paul Irish blogged about the I/O session he did with Pavel Feldman, and linked to the video - definitely worth checking out!</p>
<p>Up next, a dive into each of these!</p>
I/O Boot Camp - Getting Started With HTML52011-05-18T00:00:00Zhttps://petelepage.com/blog/2011/05/io-boot-camp-getting-started-with-html5/<p><a href="https://petelepage.com/assets/bootcamp_logo.png"><img src="https://petelepage.com/assets/bootcamp_logo-300x87.png" alt="" title="bootcamp_logo" /></a></p>
<p>Last week at <a href="http://www.io-bootcamp.com/">I/O Boot Camp</a>, <a href="http://twitter.com/edr">Ernest Delgado</a> and I presented a session titled Getting Started with HTML5.</p>
<p>Rather than take folks through the laundry list cool new stuff that’s part of HTML5, we took them through a smaller subset of features, showing how we used them in a real world application - <a href="http://bit.ly/TweeterFall">TweeterFall</a>.</p>
<iframe width="560" height="349" src="http://www.youtube.com/embed/yd30Nmb3mPU" frameborder="0" allowfullscreen=""></iframe>
<p>You can watch the <a href="http://bit.ly/jg95xi">video</a>, check out the <a href="http://bit.ly/bootcamp-html5intro">slides</a> and try <a href="http://bit.ly/TweeterFall">TweeterFall</a>. When trying the slides, be sure to try them in Chrome dev, Canary or Safari to get the full 3D effects!</p>
1970's Retro Pong2011-04-06T00:00:00Zhttps://petelepage.com/blog/2011/04/1970s-retro-pong/<p><a href="https://petelepage.com/assets/pong.png"><img src="https://petelepage.com/assets/pong-300x225.png" alt="screen shot of pong" title="Pong" /></a>I started writing my first article for <a href="http://html5rocks.com/">HTML5Rocks.com</a> the other day, and as I started working on it, I wanted to do something fun with it, something that folks could play with and enjoy, but still learn something. I found a really simple <a href="http://en.wikipedia.org/wiki/Pong">Pong</a> implementation and decided that I’d re-write it in SVG, use the HTML5 audio tag and a few other little things for some extra fun. The article isn’t done yet, but I at least wanted to put the game up!</p>
<h2 id="play-pong"><a href="http://petelepage.com/scratch/pong/">Play Pong</a> <a class="direct-link" href="https://petelepage.com/blog/2011/04/1970s-retro-pong/#play-pong">#</a></h2>
<p>The little “boink” noise is me. But it actually presented an interesting challenge, you’ll probably notice that the boink doesn’t happen every time the ball hits the wall. When the ball hits a wall twice, in quick succession, the browser is still playing the first boink, and doesn’t start a second boink [sad trombone noise]. While it’s still in early draft, this is a really great example of something the <a href="http://chromium.googlecode.com/svn/trunk/samples/audio/specification/specification.html">Web Audio API</a> will be really useful for - playing sounds in quick succession.</p>
<p>Thanks to <a href="http://twitter.com/cwilso">Chris Wilson</a> for suggesting a painfully obvious optimization - I had two loops running in the game world, one for the paddle and one for the ball moving. Chris suggested one loop and tracking which key is pressed and moving both the paddle and the ball at the same time.</p>
<p>I’ve tested the game in Chrome 10, Firefox 4, Opera 11, Safari 5 and Internet Explorer 9 and it worked well in all of them.</p>
<p>The one thing I didn’t include was a scoring system, and I didn’t really see any interesting ones on the 'net anywhere. I think the next update I’ll include one that is timer based, but if anyone has any better suggestions, I’m open!</p>
I've seen the future. It's in my browser.2011-04-01T00:00:00Zhttps://petelepage.com/blog/2011/04/ive-seen-the-future-its-in-my-browser/<p><a href="https://petelepage.com/assets/HTML5_Logo_512.png"><img src="https://petelepage.com/assets/HTML5_Logo_512_thumb.png" alt="HTML5_Logo_512" title="HTML5_Logo_512" /></a>Five weeks ago, I packed up my office and made <a href="http://twitter.com/#!/petele/status/41288481976098816">a quick tweet</a> and then drove across 520, no longer a Microsoft employee. Microsoft was an awesome experience, and I’m super-proud to have been able to work with the great people that I did, and some really great products. But, after almost ten years (May 17th would have been my 10 year anniversary), I decided it was time to turn my life upside-down and try something completely different.</p>
<p>Within a week, the movers showed up to pack my condo and help me move south to San Francisco. A new challenge was awaiting me there, and something I’m really passionate and excited about. Today will mark the end of week three at Google as a Developer Advocate on the Chrome Web Store team.</p>
<p>There are a lot of aspects of the role that drew me to it and have excited to go into work every morning-for example, I get to write code again! I’m an engineer at heart, and would much rather write JavaScript than a value proposition. Heck, I’d rather write low level assembly than write another value propositions again! I get to participate in community events, conferences, hack-a-thons and meet ups (PS-if you’re looking for a speaker for an event, let me know)! But most importantly, I get to work directly with the web developer community to build awesome new web applications using open web technologies like HTML5 and CSS3.</p>
<p>I’ve been really amazed with the <a href="http://twitter.com/#!/petele/chromedevrel-11">Chrome Developer Relations team</a>-they’re absolutely brilliant and I’ll learn a lot from them (hopefully they’ll learn stuff once I get over the information overload of the new guy on the team). If you haven’t already, check out <a href="http://html5rocks.com/">http://html5rocks.com</a>, I sit within about a dozen feet of the guys who built it-not the agency, but the real people who did the work and keep the site up to date, adding new and fresh content all the time. The site is a great jumping off place for all things HTML5, and includes an overview of what’s in HTML5 (through the <a href="http://slides.html5rocks.com/">slides</a>), as well as all kinds of cool tutorials, and a code playground. I’ve started writing my first tutorial on device orientation and hope to have it up shortly!</p>
<p><a href="https://chrome.google.com/webstore"><img src="https://petelepage.com/assets/cws.png" alt="cws" title="cws" /></a>I admit that when I started I didn’t really know or understand the purpose of the <a href="https://chrome.google.com/webstore">Chrome Web Store</a>, but as I talked to the team, and learned more about what we are trying to accomplish, I realized that it’s just another mechanism for web developers to use to get their web applications out there and used by people like my Mom. It’s not about a pay wall, or browser war, but it’s about making great web experiences easier to find, giving developers a great distribution model, and putting your web applications in front of more than 120 million Chrome users world wide.</p>
<p>If you want to put your web application into the Chrome Web Store, there’s a great tutorial at <a href="http://code.google.com/chrome/webstore/docs/get_started_simple.html">http://code.google.com/chrome/webstore/docs/get_started_simple.html</a> that will walk you through everything you need to do. Basically, you create an application manifest file (a single JSON file), package a couple of icons in there, zip it up, and upload it to the Chrome Web Store. If you’ve already got a cool web app and want to get it in front of more people, this is another great opportunity!</p>
<p>If you haven’t started playing with HTML5 yet, it’s definitely time to start-there are a bunch of great tutorials on HTML5Rocks at <a href="http://www.html5rocks.com/tutorials/">http://www.html5rocks.com/tutorials/</a>. And also check out Paul Irish’s <a href="http://html5boilerplate.com/">HTML5 Boiler Plate</a>, I’ve used it quite a bit on a couple of projects so far, and it’s super helpful. And if you’re looking for inspiration-and want to see what’s possible with HTML5, check out <a href="http://makeawesomeweb.com/">http://makeawesomeweb.com/</a></p>
<p>PEte</p>
<p>PS-the Google campus, aaaa-mazing! But I’ll save that for another post later!</p>
<p>PPS-Happy April Fools Day, I don’t know what the joke will be today, but I’m excited to find out!!</p>
Top 10 Design Mistakes: #82010-06-30T00:00:00Zhttps://petelepage.com/blog/2010/06/top-10-design-mistakes-8/<p>Time to continue my top ten list of web site pet peeves-and we’re in at number 8 with something that requires a little bit of balance, because for many sites, it’s a requirement, it’s the only way they really are able to provide the content that they do, for the price we pay…</p>
<h2 id="%238-advertising-is-a-reality%2C-sometimes">#8 Advertising Is A Reality, Sometimes <a class="direct-link" href="https://petelepage.com/blog/2010/06/top-10-design-mistakes-8/#%238-advertising-is-a-reality%2C-sometimes">#</a></h2>
<p>Somebody once said, “there’s no such thing as a free lunch.” And the web is no different, someone has to pay for the content that we’re consuming online, whether it’s through me doing this in my own time, and paying for my own hosting, or by showing ads on a page, it takes time and money to create good content. At this point, I think everyone has accepted that for many of the sites we look at each day, we’re going to see ads in addition to the content. And when done at a reasonable level, I don’t have a problem with that-especially if the ads are targeted at me, so that I’m not seeing ads for products that aren’t really of much interest to me.</p>
<p>One of my favorite examples of a site with too many ads is <a href="http://www.thedieselstop.com/forums">The Diesel Stop forums</a><a href="https://petelepage.com/assets/dieselstop1.png"><img src="https://petelepage.com/assets/dieselstop1_thumb.png" alt="dieselstop1" title="dieselstop1" /></a>. It’s a site for fans of Ford trucks-lots of great community conversations, sharing of information, all the stuff that you’d expect from a forum site. A little while ago, they did a re-design, and added a few ads to their site. And by few, I mean, that I have to scroll over 740 pixels down my page before I can see any real content! 740 pixels! Imagine how that looks on a netbook!</p>
<p>As you build out your website, it’s really important to remember what is driving people to your site, is it the content or the ads that people want to look at? I’d be willing to bet that they’re probably not coming to your site to look at the ads! One of the important things that you need to do is find a balance-content vs. ads.</p>
<p><a href="https://petelepage.com/assets/overlayad1.png"><img src="https://petelepage.com/assets/overlayad1_thumb.png" alt="overlay-ad1" title="overlay-ad1" /></a>Another one of my pet peeves is blocking ads, ads that sit in front of the site and force you to close them before you can view the content-again, users are coming to your site for content, and ads are a way to help you subsidize your content. Making it hard for users to get to content is going to discourage them from coming back.</p>
<p>Then there are other options besides the regular ads-ask yourself when visiting a website, have you trained yourself to look past the typical ad locations and shapes that look like ads? I know I’ve gotten myself pretty well trained to ignore certain places on a page or certain shapes! The person who figures out an innovative, and unobtrusive way to get ads on a page is going to make a lot of money!</p>
<p><a href="https://petelepage.com/assets/nytimes.png"><img src="https://petelepage.com/assets/nytimes_thumb.png" alt="nytimes" title="nytimes" /></a>One site that has found a way to do that is the <a href="http://www.nytimes.com/">New York Times</a>-it’s a site that I really appreciate for it’s design and layout. They’ve done a number of things to put ads on the page, make them flow nicely into the page so that they catch my eye but don’t feel obtrusive or obnoxious.</p>
<h3 id="best-practice-suggestions">Best Practice Suggestions <a class="direct-link" href="https://petelepage.com/blog/2010/06/top-10-design-mistakes-8/#best-practice-suggestions">#</a></h3>
<ul>
<li>Make your content the primary subject of your site, it’s what drives people to your site!</li>
<li>Don’t hide content with ads and make users work to get to your content, they’ll remember that and go to other places where it’s easier to find the content they’re looking for!</li>
</ul>
<h2 id="summary">Summary <a class="direct-link" href="https://petelepage.com/blog/2010/06/top-10-design-mistakes-8/#summary">#</a></h2>
<p>Remember what drives people to your site, make the content the strong point, and integrate the ads into your site in a way that fits, putting too many ads on a page will drive people away!If you’re using an ad network like <a href="https://www.google.com/adsense/support/?hl=en&sourceid=aso&subid=ww-en-et-storefrontEN_v2_HCFooterLink&medium=link">AdSense</a> or <a href="https://pubcenter.microsoft.com/">pubCenter</a>, be sure to read through their <a href="https://www.google.com/adsense/support/bin/answer.py?hl=en&answer=171656">tips and tricks</a>, they’re interested in helping you make money, because it helps them make money! Be creative! Figure out how to be a little different-be the person who finds the next advertising opportunity.</p>
<p><strong>Don’t Forget:</strong> If I pick on your website, I apologize! It’s meant as a learning opportunity for both of us, and I’m happy to help you move from my offending list, to my best practices list! I’ve only shared a few of my favorites in this blog post, and there are plenty more out there! What are some of your favorites? Leave your favorite offending sites in the your comments! For designers who may be reading, I’d love to hear your thoughts and comments! What bugs you, what makes your life hard when working with web developers?</p>
Top 10 Design Mistakes: #92010-06-20T00:00:00Zhttps://petelepage.com/blog/2010/06/top-10-design-mistakes-9/<p>Continuing my series of top web design mistakes that are often made by web developers, lets dive into number 9.</p>
<h2 id="%239-hansel-%26-gretel-would-need-bread-crumbs-to-find-their-way!">#9 Hansel & Gretel would need bread crumbs to find their way! <a class="direct-link" href="https://petelepage.com/blog/2010/06/top-10-design-mistakes-9/#%239-hansel-%26-gretel-would-need-bread-crumbs-to-find-their-way!">#</a></h2>
<p>Have you ever visited a site where you’re not quite sure what you’re supposed to do once you land on the site? Or that as your surfing around, without using the back button in your browser, you can’t get back to where you were before?</p>
<p>There are a couple of ways to look at this when you think about building navigation on your sites.</p>
<ul>
<li>What are the actions you want visitors or users of your site to take?</li>
<li>What’s the work flow for what they’re going to need to do?</li>
<li>If there’s something you want them to read or learn about, are you making that easy to find?</li>
<li>Is the navigation around your site easy to use and easy to find?</li>
</ul>
<p>Are you making those options easy for them to find and accomplish?</p>
<h3 id="the-offenders">The Offenders <a class="direct-link" href="https://petelepage.com/blog/2010/06/top-10-design-mistakes-9/#the-offenders">#</a></h3>
<p><a href="http://lesailes.hermes.com/us/en/" title="http://lesailes.hermes.com/us/en/"><strong>http://lesailes.hermes.com/us/en/</strong></a></p>
<p><a href="http://lesailes.hermes.com/us/en/"><img src="https://petelepage.com/assets/Hermes.png" alt="Hermes" title="Hermes" /></a></p>
<p>Now maybe the problem is that I’m not exactly the target audience for Hermes, about the only thing I know about them is from The Devil Wears Prada, and that they make silk scarves, but I’m sure they do more than that. So I figured I’d visit their site to see if I could learn more. When the site loads, it shows me a really pretty Flash experience, and then there’s a whole bunch of tiles that I think I’m supposed to click on, but once I do, I don’t know what the actions that I’m supposed to take are. After spending some time on the site, I really don’t know anything more about Hermes than I did before! Give it a shot, try clicking on the any of the tiles, what are you supposed to do?</p>
<p><a href="http://www.genicap.com/Site/" title="http://www.genicap.com/Site/"><strong>http://www.genicap.com/Site/</strong></a></p>
<p><a href="http://www.genicap.com/Site/"><img src="https://petelepage.com/assets/Genicap.png" alt="Genicap" title="Genicap" /></a>A friend of mine emailed me a while ago with with a link to this site. His question to me what what they heck does this company do? We both spent way too much time trying to understand the point of the site, what are they trying to sell or do?</p>
<p>There’s a bunch of text at the bottom of the screen that talks about some stuff that I don’t really understand, and the image tiles across the page, while pretty don’t really help me understand what they do. As it turns out, this company makes ray tracing tools, but I can’t find any way to purchase or learn more about their tools!</p>
<h2 id="best-practice-suggestions">Best Practice Suggestions <a class="direct-link" href="https://petelepage.com/blog/2010/06/top-10-design-mistakes-9/#best-practice-suggestions">#</a></h2>
<ul>
<li>Figure out the actions you want users to take when they visit your site, do you make those actions easy to find and easy to accomplish?</li>
<li>Make it easy to learn about you’re the point of your site–put the mission statement or other important information on the home page of your site.</li>
<li>Before starting to design your site, create a consistent navigation plan that you can implement across your site. Make it easy for folks to find what they’re looking for without having to rely on search to find what they want.</li>
</ul>
<h2 id="summary">Summary <a class="direct-link" href="https://petelepage.com/blog/2010/06/top-10-design-mistakes-9/#summary">#</a></h2>
<p>If you don’t have a good understanding of what you want your visitors to do or if you make it hard to find, users aren’t going to be able to do it. The navigation on your site needs to be consistent, and easy to follow.</p>
<p><strong>Don’t Forget:</strong> If I pick on your website, I apologize! It’s meant as a learning opportunity for both of us, and I’m happy to help you move from my offending list, to my best practices list! I’ve only shared a few of my favorites in this blog post, and there are plenty more out there! What are some of your favorites? Leave your favorite offending sites in the your comments! For designers who may be reading, I’d love to hear your thoughts and comments! What bugs you, what makes your life hard when working with web developers?</p>
<p><strong>[UPDATE 06/21/2010 9:34am PST]</strong> As I was reading my favorite blogs this morning, I came across a post about <a href="http://www.1stwebdesigner.com/development/usability-ux-pitfalls-howto-avoid/">9 Usability and UX Pitfalls, and How To Avoid Them</a> and one of the sites they listed, was the Hermes site-looks like I’m not the only one to not get it!</p>
Top 10 Design Mistakes: #102010-06-18T00:00:00Zhttps://petelepage.com/blog/2010/06/top-10-design-mistakes-10/<p>As promised, I’m going to start my series on (as my friend Sarah called it), “10 Things I Hate About Your Website”. And in reality, I admit to be guilty of many of these, so really I think I should call it “10 Things I Hate About My Websites”. This series is aimed at web developers who often find themselves having to do some design work, whether it’s just that little bit here and there, or you’re the only web person at your company this applies to you!</p>
<p>I consider myself a web developer - not a web designer. I know when a site on the web looks good, and I know when a site has kick butt usability. But, for me to accomplish both of those things on a website, probably not something I do well every time.</p>
<p>And with that, let’s jump in to number 10…</p>
<h2 id="%2310---word-writes-better-code-than-me">#10 - Word Writes Better Code Than Me <a class="direct-link" href="https://petelepage.com/blog/2010/06/top-10-design-mistakes-10/#%2310---word-writes-better-code-than-me">#</a></h2>
<p>Yep! As web developers, sometimes we focus too much on getting the job done and adding that extra cool feature instead of focusing on the code and the usability. When we write non-web code, it has to be compiled, and there’s a level of verification before it’s run, but that’s not the case for the web. You put in online, and the the browser runs it - even if there are errors in our code.</p>
<p>Sometimes, the errors in the code aren’t our fault! When I present this session with a live audience, I always ask how many folks are using a content management system, and on average about 50% of people are. When that’s the case, you can’t guarantee how the code will be spit out? Did the person who last edited that one box close their tags right, did they use the rich editor, or did they hand code their HTML after reading HTML for Dummies?</p>
<p>Now, don’t get me wrong - I personally don’t believe that my code has to pass at 100% on any of the W3C validation suites. I do believe that it should be close - 1, 2, 3, maybe 5 errors; that I can deal with. If there are more than that, I have to ask myself what’s wrong here? It’s going to make dealing rendering issues a real pain in the butt!</p>
<h2 id="the-offenders">The Offenders <a class="direct-link" href="https://petelepage.com/blog/2010/06/top-10-design-mistakes-10/#the-offenders">#</a></h2>
<p><img src="https://petelepage.com/assets/Continental1.png" alt="Continental" title="Continental" />](<a href="http://continental.com/">http://continental.com</a>)</p>
<p>**I’m a Continental flyer, I really love the airline and have had nothing but great experiences flying them, heck, I have often been known to fly out of my way to fly them! So, it kind of pains me to put them on my list of offenders, but hey, they’re an airline, not a web design shop and I can’t get too upset at them for their HTML. Their website from a design and UI perspective seems pretty good - it’s super functional, I can find what I’m looking for, get done what I need to do, and do it all pretty quickly. Seems like a good site on the surface.</p>
<p><a href="https://petelepage.com/assets/W3CContinental1.png"><img src="https://petelepage.com/assets/W3CContinental_thumb1.png" alt="W3CContinental" title="W3CContinental" /></a>But let’s try running the <a href="http://validator.w3.org/check?verbose=1&uri=http%3a%2f%2fwww.continental.com%2fweb%2fen-US%2fdefault.aspx">W3C HTML validator on the Continental web site</a>. Normally I can deal with a couple of errors, but as of the time I wrote this blog post, there are <strong>2050 errors</strong>, and if I refresh the site, the number changes!</p>
<p>That is absolutely going to make site maintenance very difficult, or trying to find out why my design isn’t looking quite like I expect - almost impossible!</p>
<h2 id="best-practice-suggestions">Best Practice Suggestions <a class="direct-link" href="https://petelepage.com/blog/2010/06/top-10-design-mistakes-10/#best-practice-suggestions">#</a></h2>
<ul>
<li>Use the W3C Validators - <a href="http://validator.w3c.org/">http://validator.w3c.org</a> to see how your code it outputting and if it would pass a “compiler” if it existed on the web.</li>
<li>If you’ve got non-web folk putting stuff online, get them trained, or put systems in place so that they can’t edit the HTML and that the tools they’re using are spitting out correct HTML</li>
<li>Be aware of your surroundings! Where is your code going to go, and how is it going to get used? That snipped you just typed, do all the tags close? Is it going into a proper container type? How is it getting used?</li>
</ul>
<h2 id="summary">Summary <a class="direct-link" href="https://petelepage.com/blog/2010/06/top-10-design-mistakes-10/#summary">#</a></h2>
<p>Keeping your code clean and professional is important - you may be the person who has to update this in 6, 12 or 18 months, but it may be someone else. Leave a legacy that you want someone to respect you for afterwards. Having messy code is going to make it maintenance near impossible! It may take some extra work in the short term, but will certainly pan out in the long term!</p>
<p><strong>PS:</strong> If I pick on your website, I apologize! It’s meant as a learning opportunity for both of us, and I’m happy to help you move from my offending list, to my best practices list! I’ve only shared a few of my favorites in this blog post, and there are plenty more out there! What are some of your favorites? Leave your favorite offending sites in the your comments! For designers who may be reading, I’d love to hear your thoughts and comments! What bugs you, what makes your life hard when working with web developers?</p>
Water Baths2010-05-21T00:00:00Zhttps://petelepage.com/blog/2010/05/water-baths/<p>Water baths are useful for affecting the shadows or highlights, depening on how you look at it. Water baths retard the development of shadow detail, so it allows highlight detail to develop while the shadow detail is “put on pause”. During normal development, the paper is placed in the developer and agitated while in the developer, allowing fresh developer to replace areas where it has been exhausted due to the shadow development. (Note, it is important to think about this process on a very small scale.) As shadows develop, they exhaust the developer much more quickly than the slow developing highlight detail. Thus, if we don’t allow that “exhausted” developer to replenish itself, it won’t be able to continue developing the shadow areas, but the developer in the highlight areas won’t have exhausted quite as quickly.</p>
<p>Using a water bath, we take the print out of the developer quite soon after we start to see the darkest part of our print show up, and we place the print in a tray of STILL water - prints must not be agitated while in the water bath, as we’ll move the developer around, and cause our shadows to continue to develop, which we don’t want them to do. If the print has been taken out of the developer too early, and we don’t get DMax on our prints, we can quickly return the print to the developer for a few seconds, and then back to the water bath.</p>
<p>Using this method, you’ll end up with shadow areas that are more open, and highlights that are more defined. Your midtones will be mostly the same, though you will likely notice some difference.</p>
<h2 id="how-to%3A-use-a-waterbath">How To: Use A Waterbath <a class="direct-link" href="https://petelepage.com/blog/2010/05/water-baths/#how-to%3A-use-a-waterbath">#</a></h2>
<ol>
<li>
<p>Immediately next to your developer tray, set up a tray (big enough to fit your print) and fill it with water. The water should be approximately the same temperature of the developer.</p>
</li>
<li>
<p>Expose your print normally</p>
</li>
<li>
<p>Place your print in the developer, when the deepest shadows on your print start to develop, GENTLY remove the print from the developer. Do NOT drain the print, we need that developer on the print to remain there so it can keep working!</p>
</li>
<li>
<p>QUICKLY & GENTLY SLIDE the waterbath.</p>
<p>Do NOT agitate the water bath.</p>
</li>
<li>
<p>Using a safe flash light, watch the print, when it looks like it’s done (no more changes in the highlights or shadows), continue to develop normally</p>
</li>
</ol>
<h2 id="notes-about-water-baths">Notes About Water Baths <a class="direct-link" href="https://petelepage.com/blog/2010/05/water-baths/#notes-about-water-baths">#</a></h2>
<ul>
<li>
<p>It is hard to determine when DMax has been reached in the dark room. It make take a few tries to determine this. Therefore, its a good idea to keep track of the total time you spend in the developer</p>
</li>
<li>
<p>You’ll need to replace the water in the water bath on a somewhat regular basis. Each time you add a new print, it’s adding developer, and eventually, you’ll end up with a very weak developer, kind of defeating the</p>
<p>purpose of a water bath</p>
</li>
<li>
<p>Sometimes areas of single tones may develope unevenly, in this case, adding a small amount of calcium carbonate (1:10) will help</p>
</li>
</ul>
Split Grade Printing2010-05-21T00:00:00Zhttps://petelepage.com/blog/2010/05/split-grade-printing/<p>Controling contrast and darks/lights is a fine art printers more challenging roles, especially when dealing with difficult negatives. Getting that highlight to come in at a nice point, and that shadow to print where it is not too stopped up can sometimes be really difficult. Split grade printing is another tool that a printer can add that will help to give them more control over their prints.</p>
<p>Using the split grade method, a print is broken up into two separate components, the highlights (which are printed at a grade 0 or 00) and the shadows (printed at a 5). By printing a portion of each print with some 00 and some 5 filter, will give fantastic highlights, great shadows and magically, the midtones just fall right in to place.</p>
<p>Using this method, you may find that difficult to print negatives, such as ones that are flat, too contrasty, require obscene amounts of dodging and burning just work, or require little fixing after their initial exposure with the zone 5 and 00 filters.</p>
<h2 id="how-to%3A-split-grade-printing">How To: Split Grade Printing <a class="direct-link" href="https://petelepage.com/blog/2010/05/split-grade-printing/#how-to%3A-split-grade-printing">#</a></h2>
<ol>
<li>Make a test strip of the ENTIRE image with the 00 filter</li>
<li>Develop, stop, fix and was as normal.</li>
<li>Select the time based on the highlight tone only. This should be done dry (or as close to dry as possible). You are selecting tone here, not tone with detail. Be careful as it is often easy to choose a tone that is darker than the one you really want, though this is easy to correct after the fact. If you don’t find a tone you like, redo the test strip either cutting your exposure or increasing as appropriate. Remember this exposure time, as it will become your base 00 exposure time.</li>
<li>Print a full image on the paper with the 00 filter. Do not remove the paper!</li>
<li>Do another test strip ON TOP of the exposed paper with a 5 filter in.</li>
<li>Develop, stop, fix and wash as normal.</li>
<li>Select the time for the 5 filter based on where your shadows look good</li>
<li>Print a new print using the 00 time and lay on top of it the 5 time</li>
<li>Develop, stop, fix and was as normal</li>
</ol>
<p>You should now have a great looking print, where your shadows fall perfectly and don’t close up, your highlights are exactly where you want them to be, and only a little touch up needs to be done.</p>
<h2 id="hints-%26-tricks">Hints & Tricks <a class="direct-link" href="https://petelepage.com/blog/2010/05/split-grade-printing/#hints-%26-tricks">#</a></h2>
<ul>
<li>If the highlights are too dark after the final print, reduce the 00 filter time</li>
<li>If the shadows are too dark after the final print, reduce the 5 filter time</li>
<li>If you dodge during the 00 filter, you’ll increase the local contrast of that area</li>
<li>If you dodge during the 5 filter, you’ll decrease the local contrast of that area</li>
<li>Thinking in terms of contrast using this method will confuse you, try to think in terms of the 00 filter giving you your highlights and the 5 filter giving you the shadow details and allowing you to hit DMax.</li>
</ul>
Pre-Development Bleaching2010-05-21T00:00:00Zhttps://petelepage.com/blog/2010/05/pre-development-bleaching/<p>Sometimes, reducing contrast is something that a printer wants, and for one reason or another cannot obtain easily. For example, using a graded paper, or having only a high contrast developer available. When this is the case, it’s possible to reduce the appearant contrast to any grade below the current value.</p>
<p>Interestingly, pre-development bleaching acts completely opposite to how post development bleaching works. Pre-development bleaching will affect the shadows first, and the highlights are left to last. This has one major advantage, in that allows you to have low contrast shadows, and high contrast highlights. Because of this, it’s useful for use on VC papers as well.</p>
<h2 id="how-to%3A-pre-development-bleaching">How To: Pre-Development Bleaching <a class="direct-link" href="https://petelepage.com/blog/2010/05/pre-development-bleaching/#how-to%3A-pre-development-bleaching">#</a></h2>
<ol>
<li>From a <a href="https://petelepage.com/blog/2010/05/pre-development-bleaching/bleach.aspx">10% stock ferri</a> solution, create a 0.1% “substock” (10ml:1000ml)</li>
<li>From the substock, create a working solution. Working solution should be somewhere between 10ml to 100ml substock to 1000ml water (0.01%-0.001% solution). A good place to start is somewhere between 10 and 30 ml substock.</li>
<li>Expose print normally</li>
<li>Insert print into working solution and agitate continiously for 1-3 minutes (be sure to use consistent times). You may need to experiment with different times and dilutions as papers act differently.</li>
<li>Discard working solution. This is a one-shot solution.</li>
<li>Develop, stop and fix normally.</li>
</ol>
<h2 id="notes">Notes <a class="direct-link" href="https://petelepage.com/blog/2010/05/pre-development-bleaching/#notes">#</a></h2>
<ul>
<li>Each type of paper will respond differently to this process, and differently to different times and different dilutions, so you may need to experiment to find what works best with your paper.</li>
</ul>
Photographic Paper Tests2010-05-21T00:00:00Zhttps://petelepage.com/blog/2010/05/photographic-paper-tests/<h2 id="maximum-development-time-test-(mdt)">Maximum Development Time Test (MDT) <a class="direct-link" href="https://petelepage.com/blog/2010/05/photographic-paper-tests/#maximum-development-time-test-(mdt)">#</a></h2>
<p>Determining the maximum development time is important to determine the point at which highlights stop developing and the paper begins to fog.</p>
<h3 id="how-to%3A-determine-mdt">How To: Determine MDT <a class="direct-link" href="https://petelepage.com/blog/2010/05/photographic-paper-tests/#how-to%3A-determine-mdt">#</a></h3>
<ol>
<li>Cut a sheet of papper into 6 strips and number them on the back 0 to 5. This should be done in safe light conditions.</li>
<li>Place the piece marked #0 directly into the fix (1 minute in bath 1, 1 minute in bath 2).</li>
<li>Place the piece marked #1 into the developer for 1 minute, then stop and fix normally.</li>
<li>Place the piece marked #2 into the developer for 2 minutes, then stop and fix normally.</li>
<li>Place the piece marked #3 into the developer for 3 minutes, then stop and fix normally.</li>
<li>Place the piece marked #4 into the developer for 4 minutes, then stop and fix normally.</li>
<li>Place the piece marked #5 into the developer for 5 minutes, then stop and fix normally.</li>
<li>Let all paper fully dry.</li>
<li>Under a bright light, examine all the strips and compare strips 1-5 with strip 0.</li>
</ol>
<h3 id="examing-the-strips">Examing The Strips <a class="direct-link" href="https://petelepage.com/blog/2010/05/photographic-paper-tests/#examing-the-strips">#</a></h3>
<p>While examing the strips under bright light, you will notice a tone on at least one of the strips. Where you start to see a tone, you have exceeded the maximum development time for that paper, and are fogging the paper.</p>
<h3 id="notes">Notes <a class="direct-link" href="https://petelepage.com/blog/2010/05/photographic-paper-tests/#notes">#</a></h3>
<ul>
<li>It may be possible that none of the pieces are showing any tone, if this is the case, repeat the test with longer times.</li>
<li>You can fine tune your test down once you’ve found approximately the minute paper begins to fog. This is especially important when your MDT is close to your MBDT.</li>
</ul>
<h2 id="mbdt">Maximum Black Development Time Test (MBDT) <a class="direct-link" href="https://petelepage.com/blog/2010/05/photographic-paper-tests/#mbdt">#</a></h2>
<p>All photographic paper has a maximum black point, where the paper after being in the developer for a certain length of time will not get any blacker. This is usually called the DMax. It is helpful to know this on papers so we know when we’ve reached our DMax and can’t get anything more out of them.</p>
<h3 id="how-to%3A-determine-mbdt">How To: Determine MBDT <a class="direct-link" href="https://petelepage.com/blog/2010/05/photographic-paper-tests/#how-to%3A-determine-mbdt">#</a></h3>
<ol>
<li>Expose a piece of paper to maximum black as determined by a test strip (the point at which you don’t see the blacks getting any darker).</li>
<li>Cut paper in to 5 strips, and label each one (1-5).</li>
<li>Place the piece marked #1 into the developer for 1 minute, then stop and fix normally.</li>
<li>Place the piece marked #2 into the developer for 2 minutes, then stop and fix normally.</li>
<li>Place the piece marked #3 into the developer for 3 minutes, then stop and fix normally.</li>
<li>Place the piece marked #4 into the developer for 4 minutes, then stop and fix normally.</li>
<li>Place the piece marked #5 into the developer for 5 minutes, then stop and fix normally.</li>
<li>Under a bright light, examine all strips and compare them to find the shortest time at which the maximum black has been hit. It may be easier to do this when the paper is still wet.</li>
</ol>
<h3 id="notes-1">Notes <a class="direct-link" href="https://petelepage.com/blog/2010/05/photographic-paper-tests/#notes-1">#</a></h3>
<ul>
<li>It is possible that the MBDT may be longer than the MDT, if this is the case, this paper/developer combination is not recommended.</li>
<li>It may be that there is no difference in blacks even at one minute, thus, it has a very fast MBDT.</li>
<li>The greater the time between the MBDT and the MDT, the greater the flexibility of the paper.</li>
</ul>
Photographic Paper Speed2010-05-21T00:00:00Zhttps://petelepage.com/blog/2010/05/photographic-paper-speed/<p>Most of these paper speeds have been obtained from the manufacturer.</p>
<table style="width: 612px;" border="1">
<tbody>
<tr>
<th style="width: 113px;"> Paper Brand</th>
<th style="width: 177px;"> Paper Type</th>
<th style="width: 145px;"> Filter or Grade</th>
<th style="width: 114px;"> ISO</th>
</tr>
<tr>
<td style="width: 113px;">Kentmere</td>
<td style="width: 177px;">Kentona</td>
<td style="width: 145px;">Grade 2</td>
<td style="width: 114px;">ISO P160</td>
</tr>
<tr>
<td style="width: 113px;">Kentmere</td>
<td style="width: 177px;">FinePrint VC (& WM)</td>
<td style="width: 145px;">Filter: 00 - 3.5<p></p>
<p>Filter: 4 -5</p></td><p></p>
<td style="width: 114px;">ISO P320
<p>ISO P160</p></td><p></p>
</tr>
<tr>
<td style="width: 113px;">Ilford</td>
<td style="width: 177px;">MG IV</td>
<td style="width: 145px;">Filter: 00 - 3
<p>Filter: 4-5</p></td><p></p>
<td style="width: 114px;">ISO P200
<p>ISO P100</p></td><p></p>
</tr>
<tr>
<td style="width: 113px;">Ilford</td>
<td style="width: 177px;">MG WT</td>
<td style="width: 145px;">Filter: 00 - 3
<p>Filter: 4-5</p></td><p></p>
<td style="width: 114px;">ISO P100
<p>ISO P50</p></td><p></p>
</tr>
<tr>
<td style="width: 113px;">FotoSpeed</td>
<td style="width: 177px;">Legacy</td>
<td style="width: 145px;">Filter: 1 - 3.5
<p>Filter: 4-5</p></td><p></p>
<td style="width: 114px;">ISO P160
<p>ISO P80</p></td><p></p>
</tr>
<tr>
<td style="width: 113px;">Oriental</td>
<td style="width: 177px;">VC-FB Warm</td>
<td style="width: 145px;">Filter: 00 - 3.5
<p>Filter: 4-5</p></td><p></p>
<td style="width: 114px;">ISO P200
<p>ISO P100</p></td>
</tr>
</tbody>
</table>
Behind the name "PEte"2010-05-21T00:00:00Zhttps://petelepage.com/blog/2010/05/pete/<p>Over time, many people have asked me why I sign my name PEte, as opposed to Pete. First off, yes, it’s intentional. 😃 It all started when I was in second year university. I think there were two factors that affected me doing it.</p>
<p>For many years, when I signed my name in email, I didn’t get my finger off the shift key fast enough, and I would sign PEte, but I’d go back and fix it. It, wasn’t a big deal, and really, I’d only do it 1 in 3 times maybe, so it was often enough to notice it, but not often enough to do anything about. One of those annoying little typing things that most people have I think.</p>
<p>Shortly there after, one of the people I was dating had a habit of writing his emails in all sorts of different capitals. For exAmPle, hiS emaIls would lOOk sOmethIng lIkE this. It was kind of fun, though a little wierd to read, and one day, I just decided to not fix the PEte that I signed my email with on an email to him.</p>
<p>I was on a work term (coop/internship that semester) and for some reason I was in one of my “whatever” moods that day, and proceeded to sign the rest of my work emails with PEte. Since then it has pretty much stuck. Every email I send these days, I sign “PEte”. I think at this point (December 2006), it’s been over 7 years that I’ve signed my name like that.</p>
<p>I’ve kept doing it for a couple of reasons, not only is it habit, but it also helps me stand out a bit. I have a pet peeve, I can’t stand fitting in the mould. I want to be close to the mould, but just different enough that I’m not in the mould, and I think this is one way I do it.</p>
<p>There you go, the story of PEte. I typically only tell people who ask, but figured it’d be a fun little story to share.</p>
<p>Take care,</p>
<p>PEte 😛</p>
Paper Flashing2010-05-21T00:00:00Zhttps://petelepage.com/blog/2010/05/paper-flashing/<p>Often times, burning in a small spot or an area with lots of different tones can be very difficult. You could burn it in, but you run the risk of letting your shadows go too dark, and not getting enough in the highlights, especially in very bright highlights such as skys, near lights, and other problematic spaces. Paper flashing is a really good way of dealing with this.</p>
<p>Paper needs a certain amount of light before it will start to show tone. Flashing is a method of providing not quite enough light to provide tone on the paper, but as soon as you expose your negative, the little bit of light in the highlight will be enough to cause the area in question show some detail.</p>
<h2 id="properties-of-flashed-prints">Properties of Flashed Prints <a class="direct-link" href="https://petelepage.com/blog/2010/05/paper-flashing/#properties-of-flashed-prints">#</a></h2>
<p>Flashing also has some other properties that may be beneficial.</p>
<ul>
<li>It will shift the exposure levels from the toe of the papers sensitivity curve further up the curve, providing detail where detail previously didn’t exist on paper (but did on the negative).</li>
<li>making further burning easier, as the paper is already close to it’s “darkening” point.</li>
<li>increasing the amount of detail visible on the paper in highlight areas.</li>
<li>extending the tonal range of graded or VC papers.</li>
<li>decreasing the overall contrast of a print.</li>
</ul>
<h2 id="how-to%3A-flash-paper">How To: Flash Paper <a class="direct-link" href="https://petelepage.com/blog/2010/05/paper-flashing/#how-to%3A-flash-paper">#</a></h2>
<ol>
<li>
<p>A test strip will need to be done to determine the maximum flash before the paper starts taking on tone.</p>
<ol>
<li>Set your enlarger at it’s maximum height, or a consistent height that you can easily remember. The best is maximum height as you need a very dim light for this test.</li>
<li>Set your lens at it’s smallest aperture, and make sure no filters are in the filter head. This test should be done with pure white light.</li>
<li>Mark several 6+ marks on your paper with a pencil or waterproof marker. You’ll use these to determine where each step in the test is.</li>
<li>Place a 10cm wide piece of paper in your easel, covering the bottom 1/3 of the paper with an easel blade. This will make your life easier later.</li>
<li>Expose each test section for a period of time. Depending on your paper, this could be anywhere beteween 1/10 of a second to 3 seconds each. I usually start at 1 second each.</li>
<li>Develop normally and dry. Yes, dry. You cannot do this test from wet paper due to dry down.</li>
<li>Determine which test zone is the first to show any kind of tone; your maximum flash point is the one before the one that shows any tone. For example, if you see tone on the 4th test zone, and each zone was 1 second, your maximum flash point is 3 seconds. Use the bit that was covered to compare against. If you’re not 100% sure which one shows a tone, cover all other zones and look again with out the other zones trying playing with your eye.</li>
</ol>
</li>
<li>
<p>Now that you’ve found the maximum flash point, expose several pieces of paper to the maximum flash point.</p>
</li>
<li>
<p>Make a normal print, including a test strip on a piece of the flashed paper. Remember, you may have to increase your contrast level.</p>
</li>
</ol>
<h2 id="notes-on-paper-flashing">Notes on paper flashing <a class="direct-link" href="https://petelepage.com/blog/2010/05/paper-flashing/#notes-on-paper-flashing">#</a></h2>
<ul>
<li>Flashing the paper will decrease your contrast, so you may need to up your contrast on a flashed print by 1/2 a point.</li>
<li>Emulsions change paper to paper. You must redo this test for each different box of paper, and each time your flash device changes. Thus, if you’re working in a communal dark room, you’ll need to do it at the start of each printing session. Or, if you’ve got your own dark room, but only one device to flash paper, you should check each and every time!</li>
<li>Sometimes, going beyond the maximum flash can be beneficial and you shouldn’t be afraid of it. It can help in troublesome areas such as sky’s, and such. It’s also useful it you will be toning the print later and want to have some ‘tone’ in that area.</li>
<li>Flashing can be done before or after the print has been exposed on the paper. In either event, it should be done with no negative in the enlarger, and no filter in the filter head.</li>
</ul>
<h2 id="the-%E2%80%98science%E2%80%99-behind-flashing">The ‘Science’ Behind Flashing <a class="direct-link" href="https://petelepage.com/blog/2010/05/paper-flashing/#the-%E2%80%98science%E2%80%99-behind-flashing">#</a></h2>
<p>The best way to describe this involves a bit of the zone system, so if you aren’t familiar with the zone system, this may not make too much sense, but hopefully it’ll get you started. Photographic paper responds in a S curve. It takes a certain amount of light for the start of the tone to appear, and once DMax (maximum black) has been reached, the paper cannot get any darker.</p>
<p>For example, say a paper starts taking on tone at 50 units of light, and hits DMax at 1200 units. (These are magic numbers designed to explain how this all works, the units are imaginary).</p>
<p>Zone 0 - 1200+ units</p>
<p>Zone 1 - 1100 units</p>
<p>Zone 2 - 1000 units</p>
<p>Zone 3 - 800 units</p>
<p>Zone 4 - 600 units</p>
<p>Zone 5 - 400 units</p>
<p>Zone 8 - 200 units</p>
<p>Zone 9 - 100 units</p>
<p>Zone 10 - 0 units</p>
<p>Now figure, I’ve got an area on my film that registers as a zone 9.7, but I want it to show up on my paper. It’s got 40 units of light, so it’s not going to show up, as we already know the paper requires at least 50 units before it will register anything. Okay, so lets add units of light to the paper by flashing it with our maximum flash (the point right before it shows detail). That will give it 49 units of light.</p>
<p>Now we’ve got 49 units of light all over the paper, and we expose our negative.</p>
<p>Our troublesome highlight that only had 40 units before, well, now it has 49+40=89 units. Woo Hoo, it’s now going to show up, it’ll be hight than a zone 9, but we’re going to at least have some hints of details!</p>
<p>What about that great shadow detail that I have at zone 2 that I have? Well, we’re only adding 40 units, so we’re not really going to shift it all that much.</p>
Film Development2010-05-21T00:00:00Zhttps://petelepage.com/blog/2010/05/film-development/<p>These are generic instructions for developing black and white film. Each film and developer combination will have slightly different times and temperatures for which they give ideal results. You will need to test your particular film and developer combination. The values given below are just recommendations.</p>
<p>One of the most important things about developing your own film is <strong>consistency</strong>. Always ensure that you do everything exactly the same as you did last time. For example, if you start your timer as you start pouring the chemistry into your tank, then make sure you do that every time. Find a groove and stick with it, it will make your life MUCH easier in the future.</p>
<h2 id="how-to%3A-develop-film">How To: Develop Film <a class="direct-link" href="https://petelepage.com/blog/2010/05/film-development/#how-to%3A-develop-film">#</a></h2>
<ol>
<li>
<p><strong>Load film onto reels in light tight development tanks.</strong></p>
<blockquote>
<p>This must be done in complete darkness</p>
</blockquote>
</li>
<li>
<p><strong>Setup Chemicals</strong></p>
<blockquote>
<p>Plastic tanks use 10oz fluid for each roll of 35mm film</p>
<p><strong>D-76</strong></p>
<p>Mix 1:1 (stock:water)</p>
<p><strong>TMax</strong></p>
<p>Mix 1:4 (stock:water)</p>
</blockquote>
</li>
<li>
<p><strong>Develop</strong> [68°F for 10 minutes]</p>
<blockquote>
<p>Agitate continiously for the first 30 seconds</p>
<p>Agitate for 5 seconds each 30 second interval thereafter</p>
<p>Pour developer down the drain - do not reuse</p>
</blockquote>
</li>
<li>
<p><strong>Stop</strong> [30 seconds]</p>
<blockquote>
<p>Agitate entire time</p>
<p>Pour stop back into stop bottle - save for reuse</p>
</blockquote>
</li>
<li>
<p><strong>Fix</strong> [3 minutes]</p>
<blockquote>
<p>Agitate continiously for the first 30 seconds</p>
<p>Agitate for 5 seconds each 30 seconds interval thereafter</p>
<p>Pour fix back into fix bottle - safe for reuse</p>
</blockquote>
</li>
<li>
<p><strong>Rinse</strong> [65-70°F for 1-2 minutes]</p>
<blockquote>
<p>Use running water</p>
</blockquote>
</li>
<li>
<p><strong>Perma-Wash</strong> [1-2 minutes]</p>
<blockquote>
<p>Pour back into Perma-Wash bottle - save for reuse</p>
</blockquote>
</li>
<li>
<p><strong>Wash</strong> [65-70°F for 5 minutes]</p>
<blockquote>
<p>Place reels in film washer and wash with water at full pressure</p>
</blockquote>
</li>
<li>
<p><strong>Photo-Flo</strong> [30-60 seconds]</p>
<blockquote>
<p>Pour back into Photo-Flo bottle - save for reuse</p>
</blockquote>
</li>
<li>
<p><strong>Dry</strong> [15 minutes]</p>
</li>
</ol>
F-Stop Printing2010-05-21T00:00:00Zhttps://petelepage.com/blog/2010/05/f-stop-printing/<p>When shooting our photographs, we all think in f-stops and zones, but when we step into the dark room, all of those principles are thrown out the window. The silly thing is, every single one of those principles apply in almost the exact same way. Paper is exposed exactly the same, add one stop of light, and you’ll get one more zone, subtract a stop and you’ll drop by one zone on your paper. Then why the heck do we think in time instead of stops? Well, because that’s what most of our timers give us. Until you get one of <a href="http://www.rhdesigns.co.uk/darkroom/html/stopclock_professional.html">these.</a> This is a timer that sets an inital “time” but then works in stops from there. Is the print too light, add a stop, or want to darken one area of the print by 1/2 a zone, then add 1/2 a stop, not 32.5 seconds.</p>
<p>Lets think about our test strips for s second.</p>
<p>Most of us create the following test strip:</p>
<table border="1">
<tbody>
<tr>
<td style="width: 39px;">5 sec</td>
<td>10 sec</td>
<td>15 sec</td>
<td>20 sec</td>
<td style="width: 48px;">25 sec</td>
<td>30 sec</td>
</tr>
</tbody>
</table>
<p>The difference between steps 1 and 2 is 1 stop. Awesome. What about 2 -> 3? Well, to be one stop, we’d need to double, and we’re not quite doing that. By the time we get to the end, the difference between steps is pretty minimal. In fact, 25 -> 30sec is only about 1/4 of a stop. Great if you need to burn something but pretty useless as test strip time.</p>
<p>Time Example:</p>
<p>For our first try, we’re going to do an 8x10:</p>
<ul>
<li>10 seconds looked good for our main subject, but we need to dodge the background a bit. We have to do another test to find out where we want background to look. Test determines it looks good at 3 seconds.</li>
<li>But now we want to do a 20x24</li>
<li>73 seconds looked good for our main subject, but we know the background needs to be done. Well, we have to do our background test all over again. This time we find it looks good at 30 seconds dodge.</li>
</ul>
<p>If we used f-stop type times, then think about the following test strip:</p>
<table border="1">
<tbody>
<tr>
<td>5.6 sec</td>
<td>8 sec</td>
<td>11 sec</td>
<td>16 sec</td>
<td>22 sec</td>
<td>32 sec</td>
</tr>
</tbody>
</table>
<p>Hey, wait a second, those numbers look somewhat familiar. We’ve increased our time in 1/2 stops and the spacing between each step is much more even! Now, lets take the example that we have a 8x10 print where the main center of focus looks good at at 11 seconds, but our background is really dark, and looks best at 5.6 seconds. Well, we know we need to dodge 1 stop. But now, we want to make a 20x24. Crap, we need to redo our test strip, and redo everything. Not really, we only need to get our main time, and know that we still need to dodge the background 1 stop. Sweet! We just saved ourselves a lot of time.</p>
<p>This can be done without an F-Stop timer, it just takes a bit more work. You’ll need to refer to an F-Stop chart to determine your times.</p>
<p>As far as I’ve found, there is only one F-Stop timer manufacturer, <a href="http://www.rhdesigns.co.uk/darkroom/html/stopclock_professional.html">RH Designs</a> based out of England.</p>
Dodging & Burning Tips2010-05-21T00:00:00Zhttps://petelepage.com/blog/2010/05/dodging-burning-tips/<ul>
<li>Use what ever works, tools, cardboard, hands, etc.</li>
<li>Unless the obvious is part of the print, work subtly, don’t let the viewer see your dodges and burns.</li>
<li>When using dodgers on a wire, and bend the wire so it goes up the light path before out of the print. This will help diffuse the light the runs along the path of the wire, helping to make it more invisible.</li>
<li>Try to follow natural lines in the print when dodging or burning.</li>
<li>Moving the dodger or burn item up and down the light path will help to diffuse and work the correction in to the print and make it look more natural.</li>
<li>Bleach may be a better alternative to dodging.</li>
<li>A foot switch will give you an extra hand.</li>
<li>With VC paper, you can dodge/burn at different filters, allowing you to show more detail in highlights or other areas.</li>
<li>Burning the sky in a bit will help enhance the ariel perspective, and give a greater realistic view.</li>
<li>Burning the edges and/or corners will help to keep the image contained and prevent it from “falling out”.</li>
<li>It may be easier to think in tones and f-stops when burning, rather than times. Though if you do use times, use percentages, as it’s easier to convert to larger images when the times comes.</li>
</ul>
Bleach Recipes2010-05-21T00:00:00Zhttps://petelepage.com/blog/2010/05/bleach-recipes/<p>The following bleaches work well for different things and are pretty useful. The
receipies are all for “stock” solutions, not working solutions. See the section
on bleaching for making working dilutions.</p>
<h2 id="potassium-ferrocyanide-(stock)">Potassium Ferrocyanide (Stock) <a class="direct-link" href="https://petelepage.com/blog/2010/05/bleach-recipes/#potassium-ferrocyanide-(stock)">#</a></h2>
<p>Keeps indefinitely in dark brown bottle.</p>
<ul>
<li>10 g Potassium Ferrocyanide</li>
<li>100 ml water</li>
</ul>
<h2 id="thiocarbamide-%E2%80%98dry%E2%80%99-reducer-(stock)">Thiocarbamide ‘dry’ Reducer (Stock) <a class="direct-link" href="https://petelepage.com/blog/2010/05/bleach-recipes/#thiocarbamide-%E2%80%98dry%E2%80%99-reducer-(stock)">#</a></h2>
<table class="table">
<thead>
<tr>
<th>Solution A</th>
<th>Solution B</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<ul>
<li>2.3g Iodine</li>
<li>50ml Mthyl alcohol</li>
</ul>
</td>
<td>
<ul>
<li>4.6g Thiocarbamide</li>
<li>50ml warm water</li>
</ul>
</td>
</tr>
<tr>
<td colspan="2">
Mix equal parts A & B just before use. To use, apply with a fine brush
and swab off with methyl alcohol. Precautions should be taken using this
bleach as its highly flammable, and not good for you, so wear gloves, and
never smoke around it. Put prints directly into fresh fixer without rinsing
to make bleaching effect permanent.
</td>
</tr>
</tbody>
</table>
<h2 id="dichromate-intensifying-bleach-(stock)">Dichromate Intensifying Bleach (Stock) <a class="direct-link" href="https://petelepage.com/blog/2010/05/bleach-recipes/#dichromate-intensifying-bleach-(stock)">#</a></h2>
<table class="table">
<thead>
<tr>
<th>Solution A</th>
<th>Solution B</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<ul>
<li>10ml Potassium dichromate</li>
</ul>
</td>
<td>
<ul>
<li>10ml Hydrochloric acid (10%)</li>
</ul>
</td>
</tr>
<tr>
<td colspan="2">
Mix 1 part A to 1 part b to 6 parts water.
</td>
</tr>
</tbody>
</table>
<h2 id="cupric-sulphate-bleach-(stock)">Cupric Sulphate Bleach (Stock) <a class="direct-link" href="https://petelepage.com/blog/2010/05/bleach-recipes/#cupric-sulphate-bleach-(stock)">#</a></h2>
<ul>
<li>50g Cupric sulphate</li>
<li>6.5ml sulphuric acid concentrate</li>
<li>50g Sodium chloride</li>
<li>water to make 1000ml.</li>
</ul>
Beers Developer2010-05-21T00:00:00Zhttps://petelepage.com/blog/2010/05/beers-developer/<table class="table">
<thead>
<tr>
<th>Solution A</th>
<th>Solution B</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<ul>
<li>3000 mL Water (hot 100°F+)</li>
<li>32 g Metol</li>
<li>92 g Sodium Sulphite</li>
<li>94 g Sodium Carbonate, Anhydrous</li>
</ul>
<p>
Mix above ingredients one at a time until dissolved.<br />
Bring to 4000 mL with additional Water<br />
Allow to cool to room temperature before using.
</p>
</td>
<td>
<ul>
<li>3000 mL Water (hot 100°F+)</li>
<li>32 g Hydroquinone</li>
<li>92 gSodium Sulphite</li>
<li>124 g Sodium Carbonate, Anhydrous</li>
</ul>
<p>
Mix above ingredients one at a time until dissolved.<br />
Bring to 4000 mL with additional Water.<br />
Allow to cool to room temperature before using.</p>
</td>
</tr>
<tr>
<td colspan="2">
<b>Solution C</b>
<ul>
<li>1000 mL Water (room temperature)</li>
<li>10 g Benzotriazolate</li>
</ul>
<p>Mix above until dissolved</p>
</td>
</tr>
</tbody>
</table>
<h2 id="mixing-ratios">Mixing Ratios <a class="direct-link" href="https://petelepage.com/blog/2010/05/beers-developer/#mixing-ratios">#</a></h2>
<h3 id="8x10-tray">8x10 Tray <a class="direct-link" href="https://petelepage.com/blog/2010/05/beers-developer/#8x10-tray">#</a></h3>
<table border="single">
<tr>
<td style="width: 100px">
</td>
<td style="width: 100px; text-align: center;">
Soft</td>
<td style="text-align: center;" colspan="2">
-------></td>
<td style="width: 100px; text-align: center;">
Normal</td>
<td style="text-align: center;" colspan="2">
-------></td>
<td style="width: 100px; text-align: center;">
Hard</td>
</tr>
<tr>
<td style="width: 100px">
Contrast</td>
<td style="width: 100px; text-align: center;">
1</td>
<td style="width: 100px; text-align: center;">
2</td>
<td style="width: 100px; text-align: center;">
3</td>
<td style="width: 100px; text-align: center;">
4</td>
<td style="width: 100px; text-align: center;">
5</td>
<td style="width: 100px; text-align: center;">
6</td>
<td style="width: 100px; text-align: center;">
7</td>
</tr>
<tr>
<td style="width: 100px">
Solution A</td>
<td style="width: 100px; text-align: center;">
16</td>
<td style="width: 100px; text-align: center;">
14</td>
<td style="width: 100px; text-align: center;">
12</td>
<td style="width: 100px; text-align: center;">
10</td>
<td style="width: 100px; text-align: center;">
8</td>
<td style="width: 100px; text-align: center;">
6</td>
<td style="width: 100px; text-align: center;">
4</td>
</tr>
<tr>
<td style="width: 100px">
Solution B</td>
<td style="width: 100px; text-align: center;">
0</td>
<td style="width: 100px; text-align: center;">
2</td>
<td style="width: 100px; text-align: center;">
4</td>
<td style="width: 100px; text-align: center;">
6</td>
<td style="width: 100px; text-align: center;">
8</td>
<td style="width: 100px; text-align: center;">
10</td>
<td style="width: 100px; text-align: center;">
28</td>
</tr>
<tr>
<td style="width: 100px">
Water</td>
<td style="width: 100px; text-align: center;">
16</td>
<td style="width: 100px; text-align: center;">
16</td>
<td style="width: 100px; text-align: center;">
16</td>
<td style="width: 100px; text-align: center;">
16</td>
<td style="width: 100px; text-align: center;">
16</td>
<td style="width: 100px; text-align: center;">
16</td>
<td style="width: 100px; text-align: center;">
0</td>
</tr>
<tr>
<td style="width: 100px">
Solution C</td>
<td style="width: 100px; text-align: center;">
1</td>
<td style="width: 100px; text-align: center;">
1</td>
<td style="width: 100px; text-align: center;">
1</td>
<td style="width: 100px; text-align: center;">
1</td>
<td style="width: 100px; text-align: center;">
1</td>
<td style="width: 100px; text-align: center;">
1</td>
<td style="width: 100px; text-align: center;">
1</td>
</tr>
<tr>
<td style="width: 100px">
Total Volume</td>
<td style="width: 100px; text-align: center;">
33</td>
<td style="width: 100px; text-align: center;">
33</td>
<td style="width: 100px; text-align: center;">
33</td>
<td style="width: 100px; text-align: center;">
33</td>
<td style="width: 100px; text-align: center;">
33</td>
<td style="width: 100px; text-align: center;">
33</td>
<td style="width: 100px; text-align: center;">
33</td>
</tr>
</table>
<h3 id="11x14-tray">11x14 Tray <a class="direct-link" href="https://petelepage.com/blog/2010/05/beers-developer/#11x14-tray">#</a></h3>
<table border="1">
<tr>
<td style="width: 100px; height: 21px;">
</td>
<td style="width: 100px; text-align: center; height: 21px;">
Soft</td>
<td style="height: 21px; text-align: center;" colspan="2">
-------></td>
<td style="width: 100px; text-align: center; height: 21px;">
Normal</td>
<td style="height: 21px; text-align: center;" colspan="2">
-------></td>
<td style="width: 100px; text-align: center; height: 21px;">
Hard</td>
</tr>
<tr>
<td style="width: 100px; height: 21px;">
Contrast</td>
<td style="width: 100px; height: 21px; text-align: center;">
1</td>
<td style="width: 100px; height: 21px; text-align: center;">
2</td>
<td style="width: 100px; height: 21px; text-align: center;">
3</td>
<td style="width: 100px; height: 21px; text-align: center;">
4</td>
<td style="width: 100px; height: 21px; text-align: center;">
5</td>
<td style="width: 100px; height: 21px; text-align: center;">
6</td>
<td style="width: 100px; height: 21px; text-align: center;">
7</td>
</tr>
<tr>
<td style="width: 100px">
Solution A</td>
<td style="width: 100px; text-align: center;">
24</td>
<td style="width: 100px; text-align: center;">
21</td>
<td style="width: 100px; text-align: center;">
18</td>
<td style="width: 100px; text-align: center;">
15</td>
<td style="width: 100px; text-align: center;">
12</td>
<td style="width: 100px; text-align: center;">
9</td>
<td style="width: 100px; text-align: center;">
6</td>
</tr>
<tr>
<td style="width: 100px">
Solution B</td>
<td style="width: 100px; text-align: center;">
0</td>
<td style="width: 100px; text-align: center;">
3</td>
<td style="width: 100px; text-align: center;">
6</td>
<td style="width: 100px; text-align: center;">
9</td>
<td style="width: 100px; text-align: center;">
12</td>
<td style="width: 100px; text-align: center;">
15</td>
<td style="width: 100px; text-align: center;">
42</td>
</tr>
<tr>
<td style="width: 100px">
Water</td>
<td style="width: 100px; text-align: center;">
24</td>
<td style="width: 100px; text-align: center;">
24</td>
<td style="width: 100px; text-align: center;">
24</td>
<td style="width: 100px; text-align: center;">
24</td>
<td style="width: 100px; text-align: center;">
24</td>
<td style="width: 100px; text-align: center;">
24</td>
<td style="width: 100px; text-align: center;">
0</td>
</tr>
<tr>
<td style="width: 100px">
Solution C</td>
<td style="width: 100px; text-align: center;">
1.5</td>
<td style="width: 100px; text-align: center;">
1.5</td>
<td style="width: 100px; text-align: center;">
1.5</td>
<td style="width: 100px; text-align: center;">
1.5</td>
<td style="width: 100px; text-align: center;">
1.5</td>
<td style="width: 100px; text-align: center;">
1.5</td>
<td style="width: 100px; text-align: center;">
1.5</td>
</tr>
<tr>
<td style="width: 100px">
Total Volume</td>
<td style="width: 100px; text-align: center;">
49.5</td>
<td style="width: 100px; text-align: center;">
33</td>
<td style="width: 100px; text-align: center;">
33</td>
<td style="width: 100px; text-align: center;">
33</td>
<td style="width: 100px; text-align: center;">
33</td>
<td style="width: 100px; text-align: center;">
33</td>
<td style="width: 100px; text-align: center;">
33</td>
</tr>
</table>
<h3 id="16x20-tray">16x20 Tray <a class="direct-link" href="https://petelepage.com/blog/2010/05/beers-developer/#16x20-tray">#</a></h3>
<table border="1">
<tr>
<td style="width: 100px">
</td>
<td style="width: 100px; text-align: center;">
Soft</td>
<td style="text-align: center;" colspan="2">
-------></td>
<td style="width: 100px; text-align: center;">
Normal</td>
<td style="text-align: center;" colspan="2">
-------></td>
<td style="width: 100px; text-align: center;">
Hard</td>
</tr>
<tr>
<td style="width: 100px">
Contrast</td>
<td style="width: 100px; text-align: center;">
1</td>
<td style="width: 100px; text-align: center;">
2</td>
<td style="width: 100px; text-align: center;">
3</td>
<td style="width: 100px; text-align: center;">
4</td>
<td style="width: 100px; text-align: center;">
5</td>
<td style="width: 100px; text-align: center;">
6</td>
<td style="width: 100px; text-align: center;">
7</td>
</tr>
<tr>
<td style="width: 100px">
Solution A</td>
<td style="width: 100px; text-align: center;">
32</td>
<td style="width: 100px; text-align: center;">
28</td>
<td style="width: 100px; text-align: center;">
24</td>
<td style="width: 100px; text-align: center;">
20</td>
<td style="width: 100px; text-align: center;">
16</td>
<td style="width: 100px; text-align: center;">
12</td>
<td style="width: 100px; text-align: center;">
8</td>
</tr>
<tr>
<td style="width: 100px">
Solution B</td>
<td style="width: 100px; text-align: center;">
0</td>
<td style="width: 100px; text-align: center;">
4</td>
<td style="width: 100px; text-align: center;">
8</td>
<td style="width: 100px; text-align: center;">
12</td>
<td style="width: 100px; text-align: center;">
16</td>
<td style="width: 100px; text-align: center;">
20</td>
<td style="width: 100px; text-align: center;">
56</td>
</tr>
<tr>
<td style="width: 100px">
Water</td>
<td style="width: 100px; text-align: center;">
32</td>
<td style="width: 100px; text-align: center;">
32</td>
<td style="width: 100px; text-align: center;">
32</td>
<td style="width: 100px; text-align: center;">
32</td>
<td style="width: 100px; text-align: center;">
32</td>
<td style="width: 100px; text-align: center;">
32</td>
<td style="width: 100px; text-align: center;">
0</td>
</tr>
<tr>
<td style="width: 100px">
Solution C</td>
<td style="width: 100px; text-align: center;">
2</td>
<td style="width: 100px; text-align: center;">
2</td>
<td style="width: 100px; text-align: center;">
2</td>
<td style="width: 100px; text-align: center;">
2</td>
<td style="width: 100px; text-align: center;">
2</td>
<td style="width: 100px; text-align: center;">
2</td>
<td style="width: 100px; text-align: center;">
2</td>
</tr>
<tr>
<td style="width: 100px">
Total Volume</td>
<td style="width: 100px; text-align: center;">
66</td>
<td style="width: 100px; text-align: center;">
66</td>
<td style="width: 100px; text-align: center;">
66</td>
<td style="width: 100px; text-align: center;">
66</td>
<td style="width: 100px; text-align: center;">
66</td>
<td style="width: 100px; text-align: center;">
66</td>
<td style="width: 100px; text-align: center;">
66</td>
</tr>
</table>
CSS Celebrates 10 years and Problems With BHOs & Tool Bars?2006-12-19T00:00:00Zhttps://petelepage.com/blog/2006/12/css-celebrates-10-years-and-problems-with-bhos-tool-bars/<p>We just moved into our new building here on campus (we moved from 42 (good) to 17 (not so good)). I’ve spent most of the day unpacking so far, and haven’t gotten much else done yet. Though I’m back to a window office. I’m right at the edge here on getting a window office. I’ve been here 5.5 years, and 5 seems to be about the magic number for this tema. The question now becomes how long will I get to keep it until someone more senior comes along. In any event, this new building is interesting. The view out of my window is great, the fact that I CANNOT find the stairs for the life of me, drives me up the wall. I’m the kind of guy who doesn’t like taking an elevator for less than 5 floors. I’m on the third, so I’d rather take the stairs. In this building, I’ve not been able to consistently find them yet. God forbid if there is a fire or anything like that, I’m screwed!</p>
<p>I also thought it would be neat to throw out a happy birthday to CSS! The W3C, in a <a href="http://www.w3.org/2006/12/css10-pressrelease.html">press release</a> today announced that CSS is now 10 years old. Very neat, though a little scary.</p>
<p>Also, I came across a blog post on <a href="http://blogs.msdn.com/tonyschr/">Tony Schreiner’s blog</a> about <a href="http://blogs.msdn.com/tonyschr/archive/2006/12/08/my-toolbar-or-bho-is-causing-ie7-on-vista-to-crash-on-close-help.aspx">problems with BHO’s and Toolbars</a>. If you’re working on one, and you’re running into problems, it might be worth checking out!</p>
Week 4 as a Product Manager2006-10-02T00:00:00Zhttps://petelepage.com/blog/2006/10/week-4-as-a-product-manager/<p>One of the people I report to this week asked me what the difference between a Product Manager and a GREAT Product Manager. It turns out the difference is in how you answer a question. Instead of saying how difficult something is going to be to do, you just say, “consider it done!” It is kind of the culture of Microsoft, don’t say no, ask how high. In order to do a great job here, you have to realize that you’re going to be asked to do the difficult. Notice I didn’t say you’ll be asked to do the impossible, there is a difference. I haven’t been asked to do the impossible, I’ve been asked to do the difficult. And so far, I’ve kept my head above water. 😃</p>
<p>I’ve learned a few valuable lessons so far. We have an alias for all the people who report to my lead’s boss. Generally the email alias is used to gather info, or share info, but just as importantly, it’s used to make fun of those people who don’t lock their computers. In my old product group, nothing like that ever took place, they were a tonne of fun, but there weren’t too many pranks going on. It seems to be that the group up stairs likes to play pranks. One woman came back a from a few days away to find her office tinfoiled. The WHOLE thing. Pens included.</p>
<p>A lot of things have crossed my plate in the last few weeks, and I’m not able to blog as often as I’d like. It’s not that there isn’t stuff to blog about, it’s more that there isn’t time to do it. I’m currently working on getting two events together, <a href="http://www.theajaxexperience.com/">An Ajax Experience</a> in Boston October 23-26 and <a href="https://ftponline.com/conferences/webdesignworld/2006/boston/">Web Design World</a> again in Boston, December 11-13. I’m also REALLY excited to hear they announced a <a href="http://north.webdirections.org/">Web Directions - North</a> in Vancouver, February 6-10 2007. If you’ve not been to Vancouver, I’d highly recommend it, it’s a beautiful city! I’ve found a great <a href="http://upcoming.org/group/16/?allevents=1">web page</a> on <a href="http://upcoming.org/">Upcoming.org</a> that lists all kinds of upcoming web conferences that I’m using to help keep track of what’s going on in the web conference world.</p>
<p>One of the other topics that I’m trying to work on is creating a IE Resource Kit CD/DVD. I’m not sure what it’s going to entail yet, or what will be on it, but it’s something that I think would be fantastic for developers! I want to try to have it done for WDW, but I’m not 100% sure I’m going to be able to get that done. If there is anything you can think of, let me know! I’d love to add it.</p>
<p>I’ve also moved to the 4th floor of my building. In my five years at Microsoft, I’ve lost track of the number of offices I’ve had, but I think this is the 9th office I’ve been in. Yes, 9 office moves. Hopefully I’ll get to keep this one for a while, but I’m not holding my breath! The crazy thing about the 9 office moves is that they’ve all be in one building. I’ve never left building 42. Most of my friends have been in 3 or 4 buildings in their time here at Microsoft, and think it’s crazy that I’ve never left this building. I just keep telling them it’s because I’m in the building that is the answer.</p>
<p>And I gotta say, I’ve been an iPod guy for YEARS. But this Zune thing, it excites me. A LOT. A few friends have them already for testing, and as much as I love my iPod, it’s going up for sale in the near future. The next question is how to diverge myself from all the songs I’ve bought on the iTunes Music Store. (BTW: Check out the Scissor Sisters new CD!!!)</p>
Day 5 as a Product Manager2006-09-11T00:00:00Zhttps://petelepage.com/blog/2006/09/day-5-as-a-product-manager/<p>I usually try to post in my blog at least once a week, but I admit, I’ve been a little lacking the last few weeks. I’m now on day 5 as a Product Manager, and not surprisingly, I still don’t know what the heck I’m doing! 😃 That’s fine though, I didn’t really expect to come on and be 100% productive the first week either. And I don’t think <a href="http://blogs.msdn.com/bgold/default.aspx">Brian</a> did either, at lease lets hope he didn’t 😉</p>
<p>There are plenty of things that I’m working on and trying to figure out. For example, the <a href="http://msdn.microsoft.com/ie/">IE Developer Center</a> has had some great work done to it, but still needs plenty more. Thus, there is a group of us working on that. I’m also trying to figure out what content is missing from the site. I’m also working on getting more information about building AddOn’s to the site. There aren’t many documents about how to create AddOn’s and the power that they have, so that’s another area.</p>
<p>Another focus area that I have right now is how to interact with the real world. We don’t have a great group of developers and designers who can help us to build a better browser. I’ve been investigating conferences, events, and people who we should be working more closely with to create a better web.</p>
<p>So, I’m still here. I’ve got an idea where I’m going, but still have plenty to learn. Keep an eye out for me!</p>
Thanks for all the fish!2006-08-29T00:00:00Zhttps://petelepage.com/blog/2006/08/thanks-for-all-the-fish/<p>One of my favourite things about working for this company is the interaction I get to have with all of you, the end users, the developers, the IT professionals, the knowledge workers and so forth. In fact, over the least two or three years, I’ve been working hard to make that more part of my daily life here as a tester on Visual Web Developer. But, being a tester, I have a primary task, and interacting with the community wasn’t it. And trust me, I tried as hard as I could to make it my primary task.</p>
<p>But, a few weeks ago, <a href="http://blogs.msdn.com/bgold/">Brian Goldfarb</a>, a Lead Product Manager for Developer Marketing sent me a link to a job he had open, the Product Manager for Internet Explorer. After reading through the job description, I knew it was something that I wanted to do. It’s a career that lets me interact with the developer community on a much more regular basis, and find ways to make IE better, enable our developers to have richer development experience.</p>
<p>Brian recruited <a href="http://blogs.msdn.com/keiths/">Keith Smith</a>, our former test manager to come work for him last October as the Product Manager for Visual Web Developer, so making this role change wasn’t something that is unheard of. I used it as a great opportunity to talk to Keith, Brian, and several of Brian’s other direct reports. After learning more about the role and getting a good introduction to Internet Explorer, I decided to take the plunge and formally ask for permission. I felt kind of bad doing it, as our new test manager, the guy who replaced Keith, had just started. In fact, I asked for permission to interview on his first day on the job!</p>
<p>And that brings us to today. I’m closing down on my test responsibilities and getting ready to leave the team. It’s something that is bitter sweet. I’ve enjoyed working with a group of extremely smart people. Each one of them has something great to offer, and is fun to work with. I’m going to miss those guys. Tomorrow is my last day on the team, though we “celebrated” (is that the right word?) that last Friday with a small party out in one of the sports fields.</p>
<blockquote>
<p>SideStory: Right before I cut the cake, Vinay (our test manager) and Brad (one of the dev managers) decided it would be fun to spray me with some champagne. The last time I had a champagne fight was just after we shipped VS2002, so it was over due. Thankfully, they didn’t get me too bad, and it was a warm day, so it dried pretty quickly. I set about cutting the cake, a really nice chocolate cake (anybody see where this is going). I cut the first piece, put it on a plate, turned around and ran at Vinay, covering his face with a piece of chocolate cake. Revenge is sweet (mmmmm, chocolate icing)!</p>
</blockquote>
<p>What isn’t going to change is you, the community I’m going to be dealing with is very similar, it’s the web designers, the add-in developers and other web developers. So stick around, the content on my blog likely won’t change much, I’m still going to be dealing with HTML, CSS, but now I’ll likely be adding more Jscript, and even some plug in stuff. I’d love to hear from you want you want to know about! What’s missing in the documentation, what bugs didn’t we fix in IE7 that you want fixed in future versions, and so forth.</p>
<p>If you’ve not checked out <a href="http://www.microsoft.com/windows/ie/default.mspx">IE7</a> yet, I also encourage you to do that! It’s been a while, but we’re finally bringing the browser into the 21st century! Another great place to get some more info is the <a href="http://blogs.msdn.com/ie/">IE blog</a>! They’ve done some great work there to spread the info about their team, and all the great stuff they’re working on!</p>
Eight Problems That Haven't Changed [On The Web]2006-06-13T00:00:00Zhttps://petelepage.com/blog/2006/06/eight-problems-that-havent-changed-on-the-web/<p>One of my <a href="http://www.unbf.ca/its/blogs/bcs">friends</a> from my <a href="http://www.unb.ca/">UNB</a> days has <a href="http://www.unbf.ca/its/blogs/bcs/2006/06/as_bad_as_ever.php">posted</a> a link to a rather interesting article titled <a href="http://www.webmonkey.com/webmonkey/06/24/index4a.html?tw=design">Eight Problems That Haven’t Changed</a>.</p>
<p>I read it this afternoon and found it to be pretty fascinating. I agree with almost all of them but sort of disagree with one. From the article, they list the 8 problems as:</p>
<ol>
<li>Links that don’t change colour when visited</li>
<li>Breaking the back button (Huge pet peeve of mine)</li>
<li>Opening new browser windows</li>
<li>Pop up windows</li>
<li>Design elements that look like advertisements</li>
<li>Violating web-wide conventions</li>
<li>Vaporous content and empty hype</li>
<li>Desnse content and unscannable text</li>
</ol>
<p>I think number two is my all time pet peeve. Well, that and sites that include music on the page (one reason why I can’t stand <a href="http://myspace.com/">myspace.com</a>). The back button is my guide to where I was, and life in general. It’s like this giant undo button. You end up on a site you don’t like, boom, gone, with one click!</p>
<p>As for opening new windows, this one I’m not so sure I agree with. In this age of tabbed browsing, why not spit someone off to a new window so that they can very easily get back to your site, or the place they were immediately before they left. To me, it’s like the magic back button in the sky. As soon as a new window opens, I generally assume I’ve left the property I was looking at, and am now at a whole new place. It gives me a feeling of a change in pace.</p>
<p>A fascinating article, worth the read!</p>
The Bellagio Fountains of Diet Coke2006-06-05T00:00:00Zhttps://petelepage.com/blog/2006/06/the-bellagio-fountains-of-diet-coke/<p>As I was doing some of my morning infosnacking, I came across <a href="http://science.slashdot.org/article.pl?sid=06/06/04/2157208">this</a> post on <a href="http://slashdot.org/">slashdot</a>. A few guys seem to have figured out how to recreate the Bellagio Fountains using only 2L bottles of diet coke, and several menthos.</p>
<p>Check out the video at <a href="http://eepybird.com/dcm1.html">http://eepybird.com/dcm1.html</a></p>
personal: this side up.2006-05-15T00:00:00Zhttps://petelepage.com/blog/2006/05/personal-this-side-up/<p>I figured I’d share this with both my personal and work blogs as it’s kind of funny and a little humbling. In my personal blog, I always write in smallcaps, so just ignore that fact, and hopefully you’ll get a laugh out of my morning.</p>
<hr />
<p>my friend heath and i have been rowing every morning for the last 6 weeks. after seeing <a href="http://imdb.com/title/tt0420206/">summer storm</a> about two months ago, we both got the itch. i rowed for a summer in university (thats where the b&w pic of me comes from). i only did it one season, but i still had a good time.</p><p></p>
<p>well, we started at <a href="http://www.lakeunioncrew.com/">lake union crew</a> in their learn to row class, and had a great time. thom, our coach was really patient, funny and entertaining. between heath, myself and a few others in the class, i think we kept the class interesting and lively for him too.</p>
<p>heath and i have since graduated from the learn to row class, and are now doing the sweep and skull program. 6:45am m/w/f mornings. let me tell you, it’s freaking early, but damn it’s fun. being out on the water, the weather has been great (for the most part), in fact today it was awesome. our new coach, has had heath and i in a boat called the marcy every morning. the marcy is a decent boat, its a 2 person skulling boat, great for beginners as it’s pretty wide and stable. we do okay in the marcy. we have a bit of a starbord strong pull, but we’ve been finding ways to combat it, and stay in a straight course (really funny using the words straight and heath and i in a boat).</p>
<p>so any ways, this morning, she puts heath and i in a new boat. the shimley. the shimley is a newer, narrower, faster boat. heath and i hop out into the water, and start going slowly, getting a feel for the new boat and such. one of the other rowers in another boat, asks us if we’ve “been in the drink yet?” we reply we haven’t and are planning on staying that way. (as if that isn’t foreshadowing) as we start to get comfortable, we start moving in this sucker. it freaking rocked. with no work, we kept it in a straight line, and we were just kicking ass and taking names (so to speak).</p>
<p>we got through the <a href="http://local.live.com/default.aspx?v=2&cp=47.647295~-122.300493&style=o&lvl=1&scene=3694252&sp=adr.5900%20W%20Green%20Lake%20Way%20N%2c%20Seattle%2c%20WA%2098103~adr.910%20N%20Northlake%20Way%2c%20Seattle%2c%20WA%2098103">montlake cut</a>, and we just finishing up a power 5, when both of us caught a crab on our starbord side… suddenly the port oars whipped out, and both of us lost our hands on the port oars. there is one golden rule in rowing: never let go of your oars or you’re going for a swim. anyone see where this is going?</p>
<p>yep, heath and i went for a swim in lake washington this morning. the water was not as cold as i was expecting, but it did take my breath away for a second. after treading water for about 30 seconds, we pulled ourselves onto the boat (which didn’t capsize, just leaned far enough for us to get out of the boat). we quickly realized that we were going to be able to get back and, and with a little work, we got back in even before our coach made it over to us. she handed us a pump and we pumped ourselves out and we’re ready to go again.</p>
<p>she was expecting us to be cold and wanting to go back in, luckily we were both wearing enough synthetics that we just took the cotton off (okay, it was only me and it was only my shirt). within a few minutes, i was dry again (my shirt wasn’t) and we were able to finish the practice. when we got back to the boat house, thom gave me a hard time about rowing without a shirt, until we explained the reason for my shirtlessness…</p>
<p>good times!!</p>
Advertising in the DVR And New Media World2006-03-01T00:00:00Zhttps://petelepage.com/blog/2006/03/advertising-in-the-dvr-and-new-media-world/<p>On my way home from work last night, I was listening to NPR when I heard a <a href="http://www.npr.org/templates/story/story.php?storyId=5238119">story about KFC’s new Buffalo Chicken Sandwich ad</a>.</p>
<p>The short version is that KFC is running an ad with a very very short scene, too short to catch without some kind of DVR. If you manage to pause at the right time, there is a cupon code to get yourself a free sandwich from KFC!</p>
<p>What an interesting way to get people to watch your ads! According to the article about 70% of people who have DVR’s skip the commercials, so the advertisers need to find new ways of getting into our homes. Well, this is certainly one way. It’s not going to work for every case (it’ll start getting old after they do it once or twice, and they depend on the hype to advertise about the fact there is something secret in their ads).</p>
<p>The interesting thing is that this ties into Mix06 quite a bit. Mix06 is all about how the next generation applicances, devices and web sites are going to market to you, and become more of a regular every day occurance!</p>
Origami Project?2006-02-24T00:00:00Zhttps://petelepage.com/blog/2006/02/origami-project/<p>You know, it’s frustrating when you don’t know what something is even when you work at the company making/doing/owning the website about it.</p>
<p><a href="http://www.origamiproject.com/1/">http://www.origamiproject.com/1/</a></p>
<p>Damn, this thing has me curious!</p>
My Fine Art Photography Web Site2005-09-19T00:00:00Zhttps://petelepage.com/blog/2005/09/my-fine-art-photography-web-site/<p>It’s alive!*</p>
<h2 id="http%3A%2F%2Fwww.bbpphoto.com"><a href="http://www.bbpphoto.com/">http://www.BBPPhoto.com</a> <a class="direct-link" href="https://petelepage.com/blog/2005/09/my-fine-art-photography-web-site/#http%3A%2F%2Fwww.bbpphoto.com">#</a></h2>
<p>My new fine art photography web site is up and working. I’ve got one more tweak to do, and then I’m all set! It’s running some special stuff. Basically I’m using a back end photogallery web application that provides an RSS feed, then using the RSS feeds to fill the front end with the data that I’m showing.</p>
<p>It may take a few minutes for DNS to finish propigating. So, if it doesn’t load, check again later this morning (PST).</p>
Too Many Domains2005-09-06T00:00:00Zhttps://petelepage.com/blog/2005/09/too-many-domains/<p>Ugh, I think I just realized I have too many domains. And I’m most of them in some way at least!</p>
<ul>
<li><a href="http://www.usualsuspects.ca/">http://www.usualsuspects.ca</a> - not in use</li>
<li><a href="http://www.bigbackpack.ca/">http://www.bigbackpack.ca</a> - in use</li>
<li><a href="http://www.bbpphoto.com/">http://www.bbpphoto.com</a> - in use</li>
<li><a href="http://www.nocommonground.com/">http://www.NoCommonGround.com</a> - in use</li>
<li><a href="http://www.sindessertco.com/">http://www.SinDessertCo.com</a> - holding for a friend</li>
<li><a href="http://www.amhconsultants.com/">http://www.AMHConsultants.com</a> - holding for a friend</li>
<li><a href="http://www.petelepage.com/">http://www.PeteLePage.com</a> - use for <a href="http://www.pcnw.org/school/workshops.php#online">teaching</a></li>
</ul>
<p>I just merged 3 accounts that I had at GoDaddy into one, since I really don’t need 3 account. No if only they would register .CA domains, I’d be all set, but in the mean time, I’ll keep the 2 .CA’s at my .CA registrar.</p>
Change to Daylight Savings Time?2005-07-20T00:00:00Zhttps://petelepage.com/blog/2005/07/change-to-daylight-savings-time/<p>Did anyone else miss this? Or am I the only one?</p>
<p>From <a href="http://www.cbc.ca/story/canada/national/2005/07/20/daylight-savings-folo050720.html">CBC</a></p>
<blockquote>
<p>_Currently in Canada and the U.S., daylight time runs from April through October. The exception in Canada is Saskatchewan, which keeps its clocks the same throughout the year. _</p>
<p>_RELATED STORY: <em><a href="http://www.cbc.ca/story/world/national/2005/07/20/daylight-savings050720.html"><em>U.S. moves to extend daylight time</em></a></em> _</p>
<p><em>The American change, due to take effect this autumn if U.S. President George W. Bush signs it into law, could cause headaches for Canadians during March and November, the two months the two countries would be out of sync.</em></p>
</blockquote>
<p>Yay for extra daylight, but it is going to be a little weird!</p>
Night and Low Light Colour Photography2005-07-12T00:00:00Zhttps://petelepage.com/blog/2005/07/night-and-low-light-colour-photography/<p>This quarter I’m taking a night, and low light colour photography class at school and its been pretty cool so far. I’ve shot about 6 rolls of film, and by far, these last two are the best I’ve picked up yet. These are just scans of the negatives, I haven’t had a chance to print them yet, so we’ll see how that goes.</p>
<p>The one on my left is my favourite I think. The scan doesn’t really do justice to the colour of the sky, but its this wonderful royal blue. When I shot this negative, it was about an 8 minute exposure, and the sky was, as far as I could tell, completely black, but when you leave the shutter open for so long, you get great colours.</p>
Welcome!2004-06-17T00:00:00Zhttps://petelepage.com/blog/2004/06/welcome/<p>As I think every other blogger has mentioned, there is always a first post, and its usually pretty lame. So here’s mine. I’m a tester on the web designer surface of VisualStudio. I’ve been at Microsoft for about 3 years now, and have been on this team the entire time. We’re working on some awesome stuff, and want to hear what you think about it.</p>
<p>I also own the Designer Smart Tasks that pop up next to a number of controls when they are dropped on the page. They’re a great way to get stuff done quickly and easily! They were a tonne of fun to test, as they are pretty dynamic, and useful!</p>
<p>My main guy in crime is <a href="http://blogs.msdn.com/bencon/">BenCon</a> who is the dev for this area. If you can convince the two of us to get something done, we’ll do our best to make sure it has some distinction in the way the product moves.</p>
Film is done2004-06-17T00:00:00Zhttps://petelepage.com/blog/2004/06/film-is-done/<p>After returning from 4 weeks in Europe, I can now say, that it took 3 weeks, but I’ve developed all 63 rolls of black and white film that I shot in Paris.</p>
<p>Now, to start making some nice fiber prints…</p>