Looking at the web after a long sleep -- observation and commentary from Michael Thomas. If you want my personal blog go to https://enervatron.blogspot.com/
Wednesday, April 3, 2019
Don't Eat Webauthn, it's Made of HOBA's!
It works pretty much like my javascript implementation, but with much better crypto, credential storage, etc, etc. But it also goes on to specify interfaces to signing dongles which is good for what it's trying to do, but I'm seriously worried that people are going to think that webauthn *requires* portable keys, biometric, etc. That would be a shame because the real enemy is not passwords, per se, it's passwords that are transmitted over the wire. And of course, their reuse. A single strong local password which is never put on a wire is perfectly fine to gain access to credentials. That's pretty much what we do today with password managers, browser fill-in-the-blanks, etc.
The reason I think that's bad is because I worry that site owners doing their own authentication will view it as niche if they think it's required to have something other a local password. Why would Epicurious implement this if they thought it was just going to be the slice of people who have access to these things? Remember, not everything is done on phones so counting on, say, biometrics only works for some of the use cases. That would be a very bad result, as webauthn using a local credential store password is a perfectly fine thing to keep others out of my super secret recipe box.
One of the things I did with my part of HOBA is elaborated quite a bit on the enrollment problem. On the tutorial sites I found, nobody seemed to mention it. I can definitely understand why the standard itself considers that out of scope (it is), but it's a little odd that it's not getting talked about much that I've found because it's probably the hardest part of deploying webauthn server-side. Maybe my Google foo failed me, but dealing with enrollment always sucks. Since webauthn is device specific, it's going to take some guidance on how to implement it server side, and users getting used to a new routine. Fortunately on the user side, we've come a long way on enrollment with more and more sites validating that the use of a new device is ok. With webauthn, that would have to be universal.
All in all, I think this is great stuff and about time. The scourge of passwords on the wire has a real chance of going away in time as sites adopt it. Let's just not hitch it to getting rid of passwords altogether. All forms of credentials have their own set of issues and tradeoffs. The important part is keeping them off the wire.
Stir/Shaken Questions
Plain Old SIP
My first question is whether this is intended to be a solution for plain old SIP URI's as well? If so, it's sort of worrisome to have his PA entity determining who can join the club for an all-IP service. Binding telephone numbers to particular carriers and making certain that the carrier is part of the telephone number club is a much different problem space than SIP-SIP with no telephone numbers involved. Assuming that is not intended to solve for that problem, is there a way to identify either the end user URI, or aggregated at provider boundaries on, say, a domain basis? With DKIM, we chose to allow mail providers to sign the mail with their domain -- whether or not they are the originating domain -- to say "blame me". This gives the originating provider some incentive not to be blamed by requiring MUA authentication, etc.RFC 7340 talks about SIP Identity (rfc 4474) and I can certainly understand the issue with B2BUA's which have analogs in the email universe (cf mailing lists). With DKIM we essentially punted and declared that the breaking gateway should sign the mail anew, and that they are now the one to blame. This seems to have worked out ok, as far I can tell. Dealing with spam is all about messy heuristics and I expect that SIP spam will be no different. The general idea is to be able get enough clues about bad actors as part of the full set of clues.
DKIM for SIP?
It has always seemed that a DKIM like approach would work for SIP as well. It has the nice property that it doesn't require a centralized CA. I'm too lazy to look up SIP identity, but I'm guessing that it does require them in some form. The web got lucky with TLS because there were only a very few browsers and the accepted CA root list was controlled by them before things could get out of control. With SIP, it's been around a long time and SIP Identity didn't get much traction from what I read, so I'm not sure if that's part of the problem, or is just academic since it just didn't get deployed.DKIM for SIP would be fairly trivial to implement. In fact, I hacked together a SIP proxy that signed and verified SIP messages. And, of course, the infrastructure is all there as DKIM at this point is really old and really widely deployed. If SIP Identity doesn't get used for whatever reason, and we don't have a deployed solution for SIP-SIP calls, that seems like a really big hole. Fortunately, we have an existence proof with email which could be retrofitted pretty easily.
Is this Partly a UI Problem?
Signer and Verifier Services
This is more of a quibble, but I really don't understand why there needs to be a separate on-the-wire protocol for SIP proxies and authentication. With DKIM, the signing and verifying are done in the MTA, and it demonstrably works just fine. The CPU cost of signing these days is minuscule, and verification was always nothing. Plus, it costs time to send something over the wire and get a response, and with SIP that is a consideration. So I just don't get what motivated this.Conclusion
While I think this looks like good work, I'm worried that it's putting a lot of time and energy into dealing with a legacy issue, and perhaps either not dealing with the SIP-SIP case, or dealing with it in a way that requires being invited to a club. Either would be very bad. We really should be trying as hard as possible to make plain old SIP the way people expect to get a "phone call", and SIP itself needs to be able to have a workable solution that doesn't depend in any way on legacy PSTN stuff or we're going to be in the same boat a few years down the line.Wednesday, March 29, 2017
Recursively Recursing the Burstorm App
Where I've Been
IT Lifecycle
- Models: when you want to just create a new design of some infrastructure and figure out who can provide it where and for how much, we call that modeling. Here, you lay out what your requirements and constraints are for your infrastructure, and you can then use the Burstorm Designer to get recommendations of providers that meet your requirements, what the cost is, and for many cloud providers even get price/performance specs on their various VM's through our performance benchmarking initiative.
- Builds: when you see a provider's offering in the Designer that looks interesting, you can create a Build from it. A Build in the Burstorm lifecycle is much more specific: it includes pricing from a particular location for a particular provider. And for cloud providers that we support, it even includes information about how your Build can be spun up.
- Contracts: you can also tell the app about infrastructure that you already have deployed with its pricing information, contract terms, etc. The app can give you help to figure out when it might be better to jump ship to another provider, as well as lots of other insights. Additionally, you can copy your current infrastructure into a Model, and start the whole process over again to redesign your current infrastructure with various tweaks you might want to make.
Our App Setup
"Burstorm App" Model
"Main App" Designer
Building Our App
Spin-Up
What the app actually does now is hands off a JSON blob describing the Build and Run specs off to a remote web server. This would be running in your infrastructure. This implementation doesn't require us to have access to your AWS keys since they'd be running on your webserver. Other arrangements could be envisioned though to make this more seamless. Either way is viable, and we're still thinking about what the best way to support this would be (if not both).
Our general strategy has been to define an API endpoint for the Burstorm App that allows programmatic access to our models, builds, product sets, etc. Our Perfrun repo takes that approach, and is the basis of the code to do the actual instance-creation/provisioning. Looking at the repo, you will see that it's just using our API like any other API consumer. In this case, it's creating VM instances, and optionally running our performance benchmark code on them.
Todo
C'est fini
Friday, July 12, 2013
Interviews as Hazing Rituals
![]() |
| Aggro Interviews Making Up for Um... |
I started this blog as a tongue in cheek kind of play on the fact that I'd been so out of the web world for so long that when I came back to look at the web, everything was so fresh and minty. Well, it turns out that that's not the only thing that has changed since I took my long snooze. I've been putting myself out again to look for a new job and I've been pretty shocked at how awful the interviewing process has become. It's become a hazing ritual straight out of your local frat house.
At some level this shouldn't be a big surprise because the entire industry -- at least what I'm seeing here in SF -- is being taken over by brogrammers. Call me old school, but I really don't want a keg on every floor and I don't see it as a feature that you use more space for ping pong tables and other toys than you do for space for your employees to work, assuming that they ever do that. And what's up with using 8' banquet tables with two people per table? It makes me want to look for severed fingers in my software because I'm surely looking at a sausage factory instead of software development.
The Greek System
But back to interviewing. One of the biggest changes nowadays is that everybody seems to be using keyword filtering software. This software doesn't appear to be especially bright as it just seems to do an intersection between the job requirements and the words that appear in your resume. If it isn't an exact intersection, you're rejected. It doesn't seem to discriminate between required, strong preference and nice to have either. If there isn't an exact match, then you're dropped. This seems especially prevalent at big companies where they force you to fill out an extensive questionnaire because, I guess, their filtering software isn't even smart enough to do the intersection function using your raw resume text. Worse, I've seen some that require you do that before you can see their job listings. I mean, seriously?
The net effect is that it's fairly useless to actually apply for a job because it never gets seen. Because, of course, there does not exist somebody who actually is an expert in the 99 things that you put on your listing as "requirements". So the unfortunately lesson here is that just dropping a resume off in the job inbox is a complete waste of time.
First, drink 10 beers
The biggest cancer -- and the subject of this rant -- are tests in the form of programming tests and puzzles. This is nothing more than a hazing ritual dressed up in all sorts of self-righteous excuses. The justification is that candidates might be lying about their programming prowess, thus it needs to be conclusively proven that you can write code on the spot. Never mind that this is extremely uncomfortable for the interviewee. Never mind that what is invariably given to be written you'd be fired for reinventing rather than just googling for the answer. Never mind the fact that most interviewers can't actually explain the problem they want you to solve. Never mind the fact that once you solve it, they want you "scale it up" whatever that means.
None of this matters because the point isn't to find out if you're a great programmer. The actual point is to convince the interviewers that they're great programmers. And superior to you in every way. Unless, I suppose, you look like them and have an affinity for drunken programming. The best part of this is when they tell you "we just want to see how you think". Call me crazy, but the only part of my thinking process you're going to understand after one of these tests is how good my rote memory is. I mean, when is the last time you've been asked to write a program to enumerate all permutations of a number? When is the last time you coded up a binary tree (no fair if you've been interviewing recently).
The absolutely worst are companies that use their hazing rituals -- like frats -- to prescreen pledges before they even a) talk to you and b) give you any clue whether you want to work in their sausage factory. This is abuse, pure and simple. And the larger joke is that it's been shown that it serves absolutely no purpose other than making the frat boys judge their penises large and manly. This google paper really drives this home, and I felt very vindicated.
We're not sure if you look like a Theta Tau
Now there are cultural reasons why you may not be a good fit, like it being a stressful job where the interviewee is looking to destress (and vise versa!). But let's be real here for a second: every company says they only hire the "best and brightest" but not a one of them is located at Lake Wobegon. How, exactly, does that work? Or is "culture" a dog whistle for "looks-like-me"? And didn't Enron pretty much prove that hiring exclusively for "best and brightest" isn't a guarantee of success? Which isn't to say that trying to get good people should be downplayed -- not at all. It's just that looking into your sausage factory I have to assume there must be some third world country where everybody is white, male, 25, and dresses in skinny jeans to escape poverty by being exceptionally smart. At companies who do exceptionally amazing things like making sepia filters for .jpg's, and virtual row crops for e-farmers.
The PI-Thon Frat House
The same thing goes for platforms and packages/frameworks. The current rage is to silo you based on whether you're iOS or Android, but it's no different than when they used to do that between M$ and Unix. Now true enough it takes time to learn a new language or platform or package/framework so it's a valid consideration to judge how much time a candidate might take to come up to speed. And true enough, there isn't much that you could do to convince me to waste my time learning .NET or some other piece of crap language/platform/package that I have no interest in learning. So, yes, thank you for putting down all of the buzz words so that I can filter too.
But where you go wrong is thinking that the lack of 7 years of mobile experience (seriously, I'm not making this up; edit: remember this is in 2013) means a priori that an otherwise competent person can't pick it up. In fact, part of the fun of a new job is getting exposure to new and different ways of approaching problems. Competent people break free of the frat they pledged to. Really.
Will nobody think of the code?
I'll pass on the spanking conga line
Friday, May 10, 2013
MVC or Not?
I'm thinking that they have similarities, but that they're not really the same as MVC as far as I understand it.
So here's what I saw coming up all the time with Phresheez.
- There's a dynamic piece of data that needs to be fetched from the server
- There's a piece of HTML content I generate from that data
- That content needs to be refreshed as new data comes into the server
- I want to use that content in multiple places, but they may want to be formatted for different uses
The rest of this post is what I'm thinking the architecture should be.
Goals
- A data abstraction layer that takes care of all of the grotty details of network errors, retransmissions, refreshing, etc, etc. Before refactoring, this code was ~duplicated all over the place each with its own subtle set of bugs. I want just one set of bugs.
- A cleanish separation between content formatting and container decoration. By container decorations, I mean things like menus/nav, titles, last updated, etc.
- I want to be able to facilitate mixing and matching different widgets. That is, I want to be able get content from widget A, widget B and widget C and mix and match them into a new super widget D.
- A recognition that there's some interplay between some of the aspects and not go crazy trying to ferret out every soi-disant layering violation
Data Layer
The Data Layer should also have error callbacks for when something goes wrong. The Data Layer must never affect the DOM in any way: it is up to its subscribers to determine what is displayed.
There may be many Data Layer objects running around with different parameters to the same underlying service. For example, you might have an object that fetches the user information for user1 and another that fetches it for user2. When the Data Layer publishes the data, it must be specific to its instance.
As an optimization, however, it would be good for the Data Layer instances to know about each other such that an instance that has the same url parameters as another will share the server's results with other Data Layer objects so as not to generate useless duplicated calls to the server. I suppose that one way to accomplish that would be for them to subscribe to each other, or maybe a global data cache layer on top of the server callbacks, but that's an implementation detail.
Container Layer
Content Binding Layer
Clicks and other Events
Inserts/Updates
So is this MVC?
Thursday, May 2, 2013
Something old, something new. Something borrowed...
The image map contains the coordinates of the polygons for all of the hexagons in the gif. So why not just use a canvas element and render it instead of snarfing up a big png/gif? So I grabbed their source, and a couple of emacs keyboard macros transmogrified the area tags and the corresponding color into a nice compact javascript array. From there it was easy: loop through the array to draw the hexagons, and in parallel create the html for the area tags. To get the image, you just call toDataURL('image/png') on the canvas, and it can be stuffed into an <img> tag.
Their picker took the selected color and sent it back to a server to show different shades of the base color. Yuck. Instead of that, I just converted the colors to hsv and created a set of variations by changing the s and v values. Simple, easy, and most importantly relatively easy to pick with fat fingers. Does it give you the ability to pick infinitely variations on a hue/saturation/luminance? No, but a) I didn't need that, and b) it would be very unwieldy for... fat fingers. Again.
The source and docs are available on google code.
Here's the source:
/* * Copyright (c) May 2 06:46:44 MTCC * Author: Michael Thomas * Module: colormap.js * Created: Thu May 2 06:46:44 2013 * Abstract:
* License: MIT
* color mapper
*/
/* Edit History:
*/
function colormap (prefix, w) {
this.prefix = prefix;
this.can = document.createElement ('canvas');
this.can.width = 234; // original image dimensions
this.can.height = 199;
if (w)
this.scale = w/this.can.width;
else
this.scale = 1;
this.can.width *= this.scale;
this.can.height *= this.scale;
}
colormap.prototype.render = function (color) {
var ctx = this.can.getContext ('2d');
ctx.scale (this.scale, this.scale);
html = '';
html += '<map name="'+this.prefix+'.map">';
for (var i in this.map) {
var slot = this.map [i];
html += '<area shape=poly onclick="'+this.prefix+'.select (\''+slot [1]+'\')" coords="';
ctx.beginPath ();
for (var j = 0; j < slot[0].length; j += 2) {
html += slot [0][j]*this.scale + ',';
html += slot [0][j+1]*this.scale;
if (j < slot [0].length-1)
html += ',';
if (j == 0)
ctx.moveTo (slot [0][j], slot [0][j+1]);
else
ctx.lineTo (slot [0][j], slot [0][j+1]);
}
ctx.lineTo (slot [0][0], slot [0][1]);
html += '"/>';
ctx.closePath ();
ctx.fillStyle = slot [1];
ctx.fill ();
}
html += '</map>';
html += x = '<br><div id="'+this.prefix+'.shades">'+this.shades (color)+'</div>';
return {html:html, img: '<img src="'+this.can.toDataURL ('image/png')+'" usemap="#'+this.prefix+'.map">'};
};
colormap.prototype.select = function (color) {
if (this.onselect)
this.onselect (color);
var el = document.getElementById (this.prefix+'.shades');
reliableNewc (el, this.shades (color));
};
colormap.prototype.shades = function (color) {
var rgb = new RGBColor (color);
var hsv = rgb2hsv (rgb.r, rgb.g, rgb.b);
var html = '';
html += '<table align=center><tr>';
for (var i = 0; i < 256; i += 16) {
if (i == 128)
html += '<tr>';
if (i < 128)
hsv.v = i;
else
hsv.s = i-128;
var rgb2 = hsv2rgb (hsv.h, hsv.s, hsv.v);
html += '<td><div onclick="'+this.prefix+'.select(\''+rgb2+'\')" style="position:relative; height:30px;width:30px; background:'+rgb2+'; border: 1px solid black"></div>';
}
html += '</table>';
return html;
};
// scraped from the source of http://www.w3schools.com/tags/ref_colorpicker.asp
colormap.prototype.map = [
[[171,180,180,184,180,195,171,199,162,195,162,184], '#993333'],
[[153,180,162,184,162,195,153,199,144,195,144,184], '#800000'],
[[135,180,144,184,144,195,135,199,126,195,126,184], '#990000'],
[[117,180,126,184,126,195,117,199,108,195,108,184], '#993300'],
[[99,180,108,184,108,195,99,199,90,195,90,184], '#CC3300'],
[[81,180,90,184,90,195,81,199,72,195,72,184], '#996600'],
[[63,180,72,184,72,195,63,199,54,195,54,184],'#663300'],
[[180,165,189,169,189,180,180,184,171,180,171,169],'#990033'],
[[162,165,171,169,171,180,162,184,153,180,153,169],'#CC0000'],
[[144,165,153,169,153,180,144,184,135,180,135,169],'#FF0000'],
[[126,165,135,169,135,180,126,184,117,180,117,169],'#FF3300'],
[[108,165,117,169,117,180,108,184,99,180,99,169],'#CC6600'],
[[90,165,99,169,99,180,90,184,81,180,81,169],'#FF9900'],
[[72,165,81,169,81,180,72,184,63,180,63,169],'#CC9900'],
[[54,165,63,169,63,180,54,184,45,180,45,169],'#996633'],
[[189,150,198,154,198,165,189,169,180,165,180,154],'#660033'],
[[171,150,180,154,180,165,171,169,162,165,162,154],'#CC0066'],
[[153,150,162,154,162,165,153,169,144,165,144,154],'#FF5050'],
[[135,150,144,154,144,165,135,169,126,165,126,154],'#FF6600'],
[[117,150,126,154,126,165,117,169,108,165,108,154],'#FF9933'],
[[99,150,108,154,108,165,99,169,90,165,90,154],'#FFCC00'],
[[81,150,90,154,90,165,81,169,72,165,72,154],'#FFFF00'],
[[63,150,72,154,72,165,63,169,54,165,54,154],'#CCCC00'],
[[45,150,54,154,54,165,45,169,36,165,36,154],'#999966'],
[[198,135,207,139,207,150,198,154,189,150,189,139],'#993366'],
[[180,135,189,139,189,150,180,154,171,150,171,139],'#CC6699'],
[[162,135,171,139,171,150,162,154,153,150,153,139],'#FF0066'],
[[144,135,153,139,153,150,144,154,135,150,135,139],'#FF6666'],
[[126,135,135,139,135,150,126,154,117,150,117,139],'#FF9966'],
[[108,135,117,139,117,150,108,154,99,150,99,139],'#FFCC66'],
[[90,135,99,139,99,150,90,154,81,150,81,139],'#FFFF66'],
[[72,135,81,139,81,150,72,154,63,150,63,139],'#CCFF33'],
[[54,135,63,139,63,150,54,154,45,150,45,139],'#99CC00'],
[[36,135,45,139,45,150,36,154,27,150,27,139],'#666633'],
[[207,120,216,124,216,135,207,139,198,135,198,124],'#990099'],
[[189,120,198,124,198,135,189,139,180,135,180,124],'#CC3399'],
[[171,120,180,124,180,135,171,139,162,135,162,124],'#FF3399'],
[[153,120,162,124,162,135,153,139,144,135,144,124],'#FF6699'],
[[135,120,144,124,144,135,135,139,126,135,126,124],'#FF9999'],
[[117,120,126,124,126,135,117,139,108,135,108,124],'#FFCC99'],
[[99,120,108,124,108,135,99,139,90,135,90,124],'#FFFF99'],
[[81,120,90,124,90,135,81,139,72,135,72,124],'#CCFF66'],
[[63,120,72,124,72,135,63,139,54,135,54,124],'#99FF33'],
[[45,120,54,124,54,135,45,139,36,135,36,124],'#669900'],
[[27,120,36,124,36,135,27,139,18,135,18,124],'#333300'],
[[216,105,225,109,225,120,216,124,207,120,207,109],'#993399'],
[[198,105,207,109,207,120,198,124,189,120,189,109],'#CC0099'],
[[180,105,189,109,189,120,180,124,171,120,171,109],'#FF33CC'],
[[162,105,171,109,171,120,162,124,153,120,153,109],'#FF66CC'],
[[144,105,153,109,153,120,144,124,135,120,135,109],'#FF99CC'],
[[126,105,135,109,135,120,126,124,117,120,117,109],'#FFCCCC'],
[[108,105,117,109,117,120,108,124,99,120,99,109],'#FFFFCC'],
[[90,105,99,109,99,120,90,124,81,120,81,109],'#CCFF99'],
[[72,105,81,109,81,120,72,124,63,120,63,109],'#99FF66'],
[[54,105,63,109,63,120,54,124,45,120,45,109],'#66FF33'],
[[36,105,45,109,45,120,36,124,27,120,27,109],'#009900'],
[[18,105,27,109,27,120,18,124,9,120,9,109],'#336600'],
[[225,90,234,94,234,105,225,109,216,105,216,94],'#660066'],
[[207,90,216,94,216,105,207,109,198,105,198,94],'#CC00CC'],
[[189,90,198,94,198,105,189,109,180,105,180,94],'#FF00FF'],
[[171,90,180,94,180,105,171,109,162,105,162,94],'#FF66FF'],
[[153,90,162,94,162,105,153,109,144,105,144,94],'#FF99FF'],
[[135,90,144,94,144,105,135,109,126,105,126,94],'#FFCCFF'],
[[117,90,126,94,126,105,117,109,108,105,108,94],'#FFFFFF'],
[[99,90,108,94,108,105,99,109,90,105,90,94],'#CCFFCC'],
[[81,90,90,94,90,105,81,109,72,105,72,94],'#99FF99'],
[[63,90,72,94,72,105,63,109,54,105,54,94],'#66FF66'],
[[45,90,54,94,54,105,45,109,36,105,36,94],'#33CC33'],
[[27,90,36,94,36,105,27,109,18,105,18,94],'#009933'],
[[9,90,18,94,18,105,9,109,0,105,0,94],'#003300'],
[[216,75,225,79,225,90,216,94,207,90,207,79],'#9900CC'],
[[198,75,207,79,207,90,198,94,189,90,189,79],'#CC00FF'],
[[180,75,189,79,189,90,180,94,171,90,171,79],'#CC33FF'],
[[162,75,171,79,171,90,162,94,153,90,153,79],'#CC66FF'],
[[144,75,153,79,153,90,144,94,135,90,135,79],'#CC99FF'],
[[126,75,135,79,135,90,126,94,117,90,117,79],'#CCCCFF'],
[[108,75,117,79,117,90,108,94,99,90,99,79],'#CCFFFF'],
[[90,75,99,79,99,90,90,94,81,90,81,79],'#99FFCC'],
[[72,75,81,79,81,90,72,94,63,90,63,79],'#66FF99'],
[[54,75,63,79,63,90,54,94,45,90,45,79],'#00FF00'],
[[36,75,45,79,45,90,36,94,27,90,27,79],'#00CC00'],
[[18,75,27,79,27,90,18,94,9,90,9,79],'#006600'],
[[207,60,216,64,216,75,207,79,198,75,198,64],'#9900FF'],
[[189,60,198,64,198,75,189,79,180,75,180,64],'#9933FF'],
[[171,60,180,64,180,75,171,79,162,75,162,64],'#9966FF'],
[[153,60,162,64,162,75,153,79,144,75,144,64],'#9999FF'],
[[135,60,144,64,144,75,135,79,126,75,126,64],'#99CCFF'],
[[117,60,126,64,126,75,117,79,108,75,108,64],'#66CCFF'],
[[99,60,108,64,108,75,99,79,90,75,90,64],'#66FFFF'],
[[81,60,90,64,90,75,81,79,72,75,72,64],'#66FFCC'],
[[63,60,72,64,72,75,63,79,54,75,54,64],'#00FF99'],
[[45,60,54,64,54,75,45,79,36,75,36,64],'#00CC66'],
[[27,60,36,64,36,75,27,79,18,75,18,64],'#339933'],
[[198,45,207,49,207,60,198,64,189,60,189,49],'#6600CC'],
[[180,45,189,49,189,60,180,64,171,60,171,49],'#6600FF'],
[[162,45,171,49,171,60,162,64,153,60,153,49],'#6666FF'],
[[144,45,153,49,153,60,144,64,135,60,135,49],'#6699FF'],
[[126,45,135,49,135,60,126,64,117,60,117,49],'#3399FF'],
[[108,45,117,49,117,60,108,64,99,60,99,49],'#33CCFF'],
[[90,45,99,49,99,60,90,64,81,60,81,49],'#00FFFF'],
[[72,45,81,49,81,60,72,64,63,60,63,49],'#00FFCC'],
[[54,45,63,49,63,60,54,64,45,60,45,49],'#00CC99'],
[[36,45,45,49,45,60,36,64,27,60,27,49],'#339966'],
[[189,30,198,34,198,45,189,49,180,45,180,34],'#666699'],
[[171,30,180,34,180,45,171,49,162,45,162,34],'#3333CC'],
[[153,30,162,34,162,45,153,49,144,45,144,34],'#3366FF'],
[[135,30,144,34,144,45,135,49,126,45,126,34],'#0066FF'],
[[117,30,126,34,126,45,117,49,108,45,108,34],'#0099FF'],
[[99,30,108,34,108,45,99,49,90,45,90,34],'#00CCFF'],
[[81,30,90,34,90,45,81,49,72,45,72,34],'#33CCCC'],
[[63,30,72,34,72,45,63,49,54,45,54,34],'#009999'],
[[45,30,54,34,54,45,45,49,36,45,36,34],'#669999'],
[[180,15,189,19,189,30,180,34,171,30,171,19],'#333399'],
[[162,15,171,19,171,30,162,34,153,30,153,19],'#3333FF'],
[[144,15,153,19,153,30,144,34,135,30,135,19],'#0000FF'],
[[126,15,135,19,135,30,126,34,117,30,117,19],'#0033CC'],
[[108,15,117,19,117,30,108,34,99,30,99,19],'#0066CC'],
[[90,15,99,19,99,30,90,34,81,30,81,19],'#0099CC'],
[[72,15,81,19,81,30,72,34,63,30,63,19],'#006699'],
[[54,15,63,19,63,30,54,34,45,30,45,19],'#006666'],
[[171,0,180,4,180,15,171,19,162,15,162,4],'#000066'],
[[153,0,162,4,162,15,153,19,144,15,144,4],'#0000CC'],
[[135,0,144,4,144,15,135,19,126,15,126,4],'#000099'],
[[117,0,126,4,126,15,117,19,108,15,108,4],'#003399'],
[[99,0,108,4,108,15,99,19,90,15,90,4],'#3366CC'],
[[81,0,90,4,90,15,81,19,72,15,72,4],'#336699'],
[[63,0,72,4,72,15,63,19,54,15,54,4],'#003366']
];
Friday, April 5, 2013
Web Look and Feel, Part 2 -- Does Native look actually matter?
The answer may seem surprising but I think that my answer would be "no". I have two different axis that are somewhat contradictory from which I draw that conclusion: 1) I'm not convinced that for the average user there is any meaningful "native" look and feel and 2) there are potential forces at work that could unify web look and feel, or at least provide a better base level set of similar look and feel, making any platform specific look and feel largely irrelevant.
What Look and Feel are We Talking About?
- Does the app navigate like a modern phone app these days?
- Does it take into account that fingers are gigantic?
- Does it take into account that people hate typing?
- Does it follow the expected for zooming and panning?
- Does the app fit the form factor instead of relying on all kinds of panning and zooming (ala unreconstructed web sites)?
- Does it count on mouse-overs to function properly (oops!)
- Etc, etc
Heretics and Heresies
- Social networks -- Facebook, Twitter
- Messaging -- email/texting
- Games/Vids
- Tracking themselves skiing (<-- kidding)
Built in Messaging Apps
Games
Back to Web Look and Feel
That said, there still is the bootstrap problem for new apps. Eventually successful apps have their own look and feel and move away from the boring native widgets, but it's useful to have them at your disposal in the beginning. With no budget. Boring is better than outright ugly, after all. And that's the big drawback of the web approach: there isn't a base level widget library to fall back onto that looks familiar and comforting, if a bit stodgy. So you have to suffer through designing all of that crap yourself, and for non-UI kind of people it's probably going to look not so great.

















