Six Months of Photography

Some six months ago, I bought a camera. I've owned cameras before, but this is my first professional camera. It has proper optics, manual settings and five million buttons. I'm officially a professional photographer.

I'm kidding of course. A good camera doesn't make a good photographer. Good football shoes doesn't make a good football player. (Obligatory parental rant #34.) But there is something special about having a really good camera. It expects you to be better. To take better photos and become a better photographer. Also, the $1500 lenses look at you very judgementally.

I've shot about 25 000 photos these first six months. I've photographed everything; cars, trees, buildings, people, my shoes, the night sky and Michelle — sitting, walking, eating, sleeping, working… For every good photo there are 50 bad ones. But that ratio is getting better. Fewer messed up exposures, poorly focused subjects and disastrous compositions. I'm learning.

“Photography is about light” is something that every photography tutorial on YouTube says. It sounds easy and intuitive, but it didn't really sink in for me until I set my camera in black and white mode. The art of photography becomes simple: everything is black unless there is light coming in. That's it. Colors trick you into thinking you have a good shot when it's really the motive that interesting, not your composition. Black and white mode asks you to create interesting photos, not just to photograph interesting subjects.

Here are some of the photos I've taken over the last six months. I'm releasing some of them on Unsplash (the blueberry one was featured, yay!). I'm also on Instagram. My camera is a Canon 5D Mark III.

Web Development on the iPad Pro

The iPad Pro deserves all the good reviews. I use it all the time, professionally and privately, and like it more than I ever expected to. I recently decided to take things one step further and try using it for web development as well.

Now, it's not all about the iPad — the magic actually happens in the cloud. I use a $5/month Ubuntu server hosted by Digital Ocean. I could probably get away with less using AWS, but I like Digital Ocean's shiny interface. I'm only human.

Blink for iOS is a great shell that allows me to SSH into my development server. The IP address is available in Digital Ocean's control panel. Swedish 4G is quite good so there is no noticeable lag.


I host my projects on GitHub. To access them, I generated a new SSH key and hooked it up to my GitHub account. After that, I can clone/push/pull projects freely. When running my build scripts, I ran into a bunch of issues with packages, compilers and dependencies. After 30 minutes of Stack Overflow and apt-geting things all my build scripts were working smoothly. Development time!

My current project involves Jekyll which serves a static web site to a local url like localhost:4000. To make the site accessible from Safari I use Localtunnel which takes any local URL and gives it a random, publicly accessible URL.

Vim is my editor of choice on the Mac, so that's how I edit files on the iPad as well. It's a pain to learn, but it's worth it — being able to feel right at home on any unix machine is pretty neat. To make sure that my build script's output doesn't interrupt Vim I send the build script's output to /dev/null.

The iOS 11 beta has even better split screen support than previous generations of iOS. Windows can be arranged in custom 25%-50%-75% arrangements with another window floating on top.


All of this makes developing on the iPad Pro pretty great. I won't use it as my main development machine but I might settle for the iPad next time I leave town for a few days.

Stockholm → Berlin

On July 29, Michelle and I are going abroad for six months. We'll move around in Europe – a few weeks in Prague, short stops in Amsterdam and Paris and some longer stays in Berlin and maybe Rome. Michelle will be studying business and code and I will continue to study math, AI and robotics. Peak excitement!

Self Driving Lego Mindstorms Robot

I have a strange fascination with all kinds of autonomous robots. One of the main goals with my math and AI studies is to be able to build my own autonomous vehicle. But that's a few linear algebra courses away… In the mean time, I happily settle for LEGO Mindstorms.

The rotating part is a sensor which detects the distance to nearby obstacles. It looks left and right, left and right, left and right… It then analyzes the best course of action and continues in that direction. If it encounters a dead end it backs up, rotates and tries to find another path.

Managing Styles and CSS in Framer

The way Framer.js handles styling of layers can be confusing. Some parts are handled using Framer's built-in layout engine, other parts using the CSS you love and hate. By understanding the relationship between the two you can write cleaner and more manageable Framer code.

What’s a Framer layer anyway?

Framer layers are the basic components of all Framer prototypes. They can contain images, text, video, graphics imported from Sketch and much more. Every layer has properties that tells Framer what the layer looks like and what happens when you interact with it.

If you are a web developer, you may be tempted to think of Framer layers as HTML elements or DOM nodes. They have similarities, but are fundamentally different. Framer uses HTML to render it’s prototypes, but you’re not setting properties on the actual HTML element – Framer does that for you.

Style properties are set on the Layer

The design properties of your layer are added directly onto each Layer object. Where a JavaScript developer may be used to writing = 'red' a Framer designer would simply write item.color = 'red'.

myLayer = new Layer

# Framer properties are added onto the layer
myLayer.x = 20
myLayer.y = 20
myLayer.width = 200
myLayer.height = 500
myLayer.borderRadius = 10

Well, not all properties…

Framer layers doesn’t support all CSS properties out of the box. Typographic styles for instance, can’t be added directly onto the layer — you need to use the styles property. So, while you can write layer.x and layer.width, you need to use the style property for and for some other properties.

myLayer = new Layer

# Framer properties
myLayer.x = 20
myLayer.y = 20
# …

# Actual CSS styles =
	'font-family' : 'helvetica, sans-serif'
	'font-weight' : 'bold'
	'text-transform': 'uppercase'

Leave element positioning to Framer

Don’t try to micromanage the styling of layers. Forget what you know about position: absolute; and translate3d(). Framer takes care of positioning for you. Try not to think about how it’s done – just set the element’s x and y properties and be happy.

Tips for working with styles in Framer

At the top of my Framer file, I typically create a preferences object which holds the global settings for my Framer app. This is a good practice that will help you write DRY Framer code.

# My Framer app's settings live here
prefs = {}

Next, I add a few default properties that I want to use throughout my app.

prefs.margin = 20
prefs.borderRadius = 3
prefs.somethingElse = 100

When creating new layers, I simply reference these properties:

header = new Layer
	x: prefs.margin
	y: prefs.margin
	borderRadius: prefs.borderRadius

This makes the app much more maintainable. Whenever I want to change the default margin or border radius, I only have to do it once, in my prefs object. This saves time and ensures that the app’s design is consistent.

Adding defaults to layers

If you’re absolutely sure that every layer inside your Framer app should have certain styles, you can set global defaults that apply to all Framer layers. Generally however, I would advise against this. It tends to add bloat to the prototype and confuse other developers.

Framer.Defaults.Layer.backgroundColor = 'red'

Adding complex CSS to the prefs object

As I mentioned earlier, some style properties are added as properties on the Layerobject. Others are added as sub-properties of the Layer’s style property. The latter is true for all typography styles. To manage this, I set up groups of CSS styles in my main prefs object:

prefs.styles = {}
prefs.styles.bodyText =
	fontFamily: 'helvetica, sans-serif'
	fontSize: '16px'
	lineHeight: '20px'

This allows me to reference this style whenever I create a new Layer:

comment = new Layer
	x: prefs.margin
	y: prefs.margin
	style: prefs.styles.bodyText

Mixing style objects

Sometimes you want to mix two different CSS objects in a single layer:

prefs.styles.largeText =
	fontSize: '44px'

prefs.styles.boldText =
	fontWeight: 'bold'

largeAndBold = new Layer
	style: /* Wanted: a mix between largeText and boldText */

To solve this, I often use the native JavaScript method Object.assign() which mixes two or more JavaScript objects to create a new object.

callout = new Layer
	style: Object.assign(,

This will create a mixed object containing properties from both largeText and boldText and assign it to the new layer’s style property.

Another way of achieving the same result is to assign values to the style property twice:

callout = new Layer = =

Somewhat surprisingly, Framer does not replace the first value with the second one. Instead, it combines the largeText and the boldText styles. Unintuitive but useful.

Styling inline elements

In Framer Studio, inline elements are created by adding a html property to the layer with the inline elements added as part of the string.

myElement = new Layer
	html: 'Your friend <span>@upperdog</span> added a new photo.'

As a JavaScript developer, you may feel the urge to use document.querySelector() to access the child element directly. Try to avoid this. Accessing DOM elements in Framer will cause a bunch of headaches in the long run.

Instead, use inline styles to apply the styles:

myElement = new Element
	html: 'Your friend <span style="font-weight: bold">@upperdog</span> added a new photo.'

Using our prefs object with inline styles

Hard-coding inline styles works well, but it doesn’t scale. The minute you start adding more inline styles to your app, things get messy. The best way to solve this is to use our trusted object. Ideally, what you would want is something like this:

myElement = new Element
	html: 'Your friend <span style="'
		+ prefs.styles.highlight
		+ '">@upperdog</span> added a new photo.'

This won’t work however, since the JavaScript object prefs.styles.highlight will be converted to [object Object] when used inside a string. To solve this, you need a function that converts your JavaScript styles to a CSS string:

styleToString = (obj) ->
	str = ''
	for key, value of obj
		str += key + ': ' + value + ';'
	str = str.replace(/"/g, "'")
	return str

Now, simply use that function to when adding styles to your inline elements.

myElement = new Element
	html: 	'Your friend <span style="'
		+ styleToString(prefs.styles.highlight)
		+ '">@upperdog</span> added a new photo.'

Again, this lets you update the prototype’s CSS in one place instead of spreading it out throughout the code.


  • Some properties are defined on the layer (layer.x), others on the style property (
  • You don’t have to think about position: absolute; or translate3d(). Framer handles that for you.
  • Put commonly used design patterns in a global prefs object. This makes your design more consistent and your code more maintainable.

Using the Device Orientation API to Estimate Proximity

The Device Orientation API of HTML5 tells us the orientation of the user’s device at any given moment. However, in some controlled environments it can also be used to guess a device’s position in the room.

A while back, I was recently tasked with designing and developing an iPad web app for a client. It was to be used as a sales tool at conventions and exhibitions and when not in the hand of a sales representative it would be mounted on the walls of the exhibition booth.

The idea was to turn the app’s own “screensaver” on as soon as the iPad was mounted on the wall, but since the iPad doesn’t have a RFID or NFC chip, how can we tell when it’s mounted on the wall? HTML5′s Device Orientation API.

The angle of the books differ slightly. Using the Device Orientation API we can tell which book the iPhone is lying on. The colors are pre-defined.

The solution is not perfect and it’s just qualified guessing, but it works remarkably well. By adding a slight delay to the screensaver we can make sure that it’s not turned on as part of a larger hand motion.

This is in no way a replacement to proper near field communication, but in our controlled environment — the exhibition booth — it was all we needed to tell if the iPad was mounted on the wall or not.