Thoughts on Engineering, Photography, and Design.

Hey, I'm Ryan Heath. I design & develop things for a living and play with cameras for fun. This is where I share my thoughts on all of that — and probably more — along the way.

Querying APIs with Vanilla JS

I’m at the point in my career where keeping things simple is a top priority, even if that means a little sacrifice in other areas. In this particular case, as part of redesigning rpheath.com, I wanted to pull in the latest N posts from this blog. Given that I’m working with a static site, that meant using JavaScript. A young whippersnapper may immediately turn to React, Vue, Svelte, or (gasp!) Angular, which is reasonable. Those frameworks make this type of thing clean and easy. But I did not want to deal with integrating webpack and/or babel into my builds, as I’ve been down that road plenty, and it’s (shockingly!) still pretty annoying to deal with. Besides, each redesign of my personal site strives for more and more simplicity, so that would go against the grain.

I chose to use vanilla JS for this. Let’s talk about it! It all starts with a fetch-based function to grab the data from the blog’s API:

1function fetchPosts(url, callbackFn) {
2  fetch(url)
3    .then(response => response.json())
4    .then(data => callbackFn(data))
5    .catch(error => console.error(error));
6}

The fetchPosts() function grabs data from an API and hands it over to a callback function that I define for processing. This is all so simple because fetch() is built on the Promise, which in modern JavaScript, is an expectation for asynchronous calls.

Since we don’t have a framework to do stuff for us, the next “problem” we need to take care of is ensuring that our code runs at the right time. Meaning, after the DOM has loaded. Old school folks may remember jQuery’s $('document').ready(...) or $(function() { ... }) helpers! Yeah, it’s that, but with vanilla JavaScript. There are two ways to approach it:

 1// (1) wait for the DOMContentLoaded event
 2document.addEventListener("DOMContentLoaded", (_e) => {
 3  console.log("The DOM is ready!");
 4});
 5
 6// (2) a self-executing function
 7(function() {
 8  console.log("The DOM is ready!");
 9  console.warn("...but only if this JS is included at the bottom of the page");
10  console.warn("...also, I ignore deferred scripts!");
11})();

Both work, but notice the caveats with option (2) above. If you go with a self-executing function, it’ll execute whenever the browser reaches it, so it must be after your HTML is parsed, top-down (and even then it still ignores deferred scripts). We’ll go with option (1) for our purposes.

The next bit to work out is what to do with our data once we get it back from the API. In React, for example, we’d make a component that spit out some HTML for the browser. Well, that’s exactly what we’ll do, only we’ll use JavaScript’s Template Strings instead.

 1const renderPosts = (data, container) => {
 2  let posts = [];
 3
 4  data.forEach((post) => {
 5    const html = `
 6      <div class="post">
 7        <div class="post-date">
 8          <time>${post.date}</time>
 9        </div>
10        <div class="post-link">
11          <a href="${post.url}">${post.title}</a>
12        </div>
13        <p class="post-caption">${post.summary}</p>
14      </div>`;
15
16    posts.push(html)
17  });
18
19  container.innerHTML = posts.join("\n");
20};
21

I bet any youngsters who stumble onto this won’t even know what innerHTML is. But essentially, we just take the data and iterate over the response, creating an array of string-interpolated items. Once we’re done “looping”, we just set the container’s HTML to render our posts.

It’s a nice touch to put some kind of default text or loading indicator into your “container” element:

1<div id="blog-posts">
2  [spinner svg] Loading Posts...
3</div>

Then once the DOM loads and your function executes, innerHTML will replace your loading content with the interpolated response from the API.

Now that all of our pieces are in place, the result looks like this:

1document.addEventListener("DOMContentLoaded", (_e) => {
2  const blogContainer = document.querySelector("#blog-posts");
3
4  // we only want to do this for the "blog" section of the site
5  if (blogContainer) {
6    fetchPosts((response) => renderPosts(response.data, blogContainer));
7  }
8};

You can see this in action by going to rpheath.com/blog.

The important lesson here, if there is one, is to avoid defaulting to complexity because “that’s what the internet does”. If I wanted to use React for this, I totally could, and the outcome would look very clean and satisfying. But the complexity involved with integrating React into my builds was just not worth it. I have the same result, but with zero configuration or headache. I’m just using what browsers already know and understand. And for me, that’s the reward.

A Personal Site Redesign

Every so many years I do a redesign of my personal site, rpheath.com. Things go stale, technically, so I clean that up and typically put a fresh coat of paint on while I’m at it. Given that I’m usually working to satisfy other people, it’s nice to work on something where I’m the only stakeholder. Ahh, creative freedom.

Something I wanted to bring back to this redesign was a bit of “fun”. Remember CSS Zen Garden? The idea was all about showcasing the power of CSS and how it could transform the same HTML into completely different designs. It got me thinking… Why does my personal site need to adhere to the same design on every page? After all, it’s not a product, it doesn’t have “users”. So that’s what I did. Each page represents its content in its own way.

I don’t know how long I’ll live with these changes, but as of today, I’m digging it. Here’s a before/after glimpse of how it turned out, but you can browse for yourself at rpheath.com.

BEFORE

AFTER

Comments are off, but if you really want to share your thoughts, try ryan@rpheath.com.

Pairing Photos

One of the things that took me a while to learn was the impact photos can have when represented as a pair. I’m generally not one to talk about “story telling” with photography (even though I believe in it), but when it comes to pairing photos, I think the story can be laid out so much better.

Take the pair of photos above, for example. Individually, they’re pretty “meh”. You get the idea, there was some nice light making shadows. But with two photos? Now you start to see the full “story”. At least I do. I can visualize this light coming through every window of the house, making shadowed shapes all over the place. The impact is double for me.

Again, another set where the individual photos are solid, but together have greater impact. Haystack Rock is massive, it’s right on the beach and everyone has seen so many photos of it from that perspective. But there’s something about showing buildings and houses that tell the full “story”. When you see one of these, maybe it’s just an anomaly. But when you see two? Then it’s like, okay, this rock is literally right in front of all these houses. At least, that’s how I think about it.

In this last example, these photos don’t even really make sense. I don’t know if there’s any “story” here at all. But that’s kind of my point: sometimes a pair of photos just look better, together. I personally really like these two, but mostly because they’re next to one another. Maybe it’s the colors? I don’t know, Portra is magical. But I do know that I like them a lot less when I see them in isolation.

So there you go. Pair your photos, it’s another trick in the bag to get more out of your photography.

Shooting Film: Mamiya C330

I knew when I finally started shooting film that 35mm would just be the gateway drug into something else. But I didn’t expect to find what I found, and I didn’t expect to love it as much as I do.

The Mamiya C330 is a medium format film camera that shoots 120 (6x6) film. It’s a twin-lens reflex design, where the top lens is used for focusing, and the bottom lens takes the photo. It focuses via bellows extension and can get almost macro-level close. You turn the knobs on the sides to move the bellows in and out.

Scale

Golden hour at a Go-Mart / storage unit parking lot

The top of the camera flips open and you look down at a viewfinder to focus the scene. There’s also a magnifying glass built-in that’s optional, but when you enable that, you can really dial in your focus. This all sounds kind of nuts, I get it, but it actually works really well. The Rolleiflex is one of the more well-known TLR cameras, but it’s expensive. Additionally, it doesn’t allow swapping lenses, which you can do on the C330. I only have one lens (a 105mm), and don’t plan to buy another, but I could.

For a camera that’s 55 years old (at the time of this writing), the photos I’m able to get out of it just blow me away. I bought this camera on eBay for about $300 (including the lens).

It’s heavy, awkward, and extremely slow to use (fully mechanical, no light meter), but it has given me a new perspective on photography. When I flip through photobooks that were made using large format cameras, for example, I now have a greater appreciation for the work that went into each frame. And while it’s so different from digital, and in many ways, “not worth it”, there’s no greater feeling than composing a scene, pressing the release cable, and finding out that you made a perfect exposure. It’s a very rewarding experience, end-to-end.

Shooting Film: Bessa R2A

There was a time when I simply could not understand what the appeal was when it came to film photography. It always intrigued me, but I was never certain that I had the patience for it and the overall process just felt so… pointless? But here I am. Years later, I have a different perspective. I recently picked up a 20+ year old, used film camera.

Scale

Voigtlander Bessa R2 w/ 35mm Nokton Classic f/1.4 II

Right off, this camera is beautiful! And if you know me, I can’t dare use a camera that doesn’t align with my aesthetic tastes.

I’m not a product review kinda guy, but it’s worth noting a few things that make this camera special:

  • The largest framelines are 35mm, which is what I plan on shooting with this camera.
  • I shoot aperture priority, which this camera has! (along with +/- 2 stop exposure compensation)
  • The rangefinder patch is excellent, despite the camera being old-ish.
  • It’s a M-mount, so I can use my existing VM lenses.

There’s a bit of anxiety that comes along with shooting film, but it does seem to make you hone in on what you’re doing because you can’t make “test exposures” to verify anything. If you over expose a scene on purpose, you won’t know if you blew those highlights until you get the film back. You’re forced to understand how different films react to light and be a lot more intentional up front, which I (so far) am enjoying.

Another thing I didn’t really think about: you sort of choose your “edit” up front. Sure, you can make small adjustments to the scans in post-processing, but for the most part, you’re choosing your colors, contrast, and overall feel up front before you head out with your camera. That’s kinda cool and very different for me.

We’ll see how this whole thing plays out. I’m still a digital shooter at heart (if nothing else because of the cost of film, sheesh), but I will say, the feeling I get when cranking the film advance after every shot is pretty addicting.

Anyway, I find that hobbies—including photography—require you to “keep it fresh” to stay motivated and inspired. Right now, for me, that’s the Bessa R2A. Wish me luck!

Composition Tip: Scale

Let’s get to it: When you’re shooting something big, think of ways to show its scale. Scale is a powerful composition trick that adds impact to a photo. When properly done, the viewer can feel like they’re in the scene vs looking at the scene.

Scale

100mm, f8, 1/1300, ISO 125

There’s nothing special about this photo, but I intentionally waited for this gliding bird to be in the scene solely because of scale. I think having that tiny bird in there shows how large the cloud actually is, as well as the rainbow.

Now, this is just a photo of some random clouds, so perhaps it’s a poor example. But imagine if this were a massive waterfall? Or a mountain? By itself, it would be a nice photo, but the viewer may not be able to comprehend actually being there. Now, if at the base of that waterfall or mountain were a tiny car, or person, or tree, or… you get the idea. The photo would feel completely different. And overall, isn’t that the goal? Make the viewer feel something (good or bad)?

Next time you’re out shooting, keep this in mind: for grandiose scenes, the impact and drama of your photography can be greatly improved if you’re considering scale.

Photographing the Moon

It seems like it should be really easy to photograph the moon, right? And I guess it is, but there are a few things to be aware of for a frustration-free experience.

Moon

560mm (400mm w/1.4x teleconverter), f8, 1/250, ISO 800

Reach!

Depending on what you’re after, it helps to have a long lens so you can do one of two things: fill the frame and/or take advantage of compression against a foreground subject. Have you ever seen those photos that look fake, where the moon is gigantic behind a… lighthouse, for example? Yeah, that’s what telephoto compression does.

A 100-400mm is a great lens to have for a lot of reasons, but can work well for moon photography.

No IBIS

Given you’re at such a long focal length, you’re probably going to need a tripod. And when you’re on a tripod, IBIS can actually have the opposite affect on your photo and cause camera shake. Although, if it’s windy, you can (maybe) consider keeping it on. The point here is to be aware that this is definitely something to think about.

Fast Shutter

Believe it or not, the moon moves very fast! Even a 10s shutter speed could result in soft images due to movement. Additionally, the moon is very bright, so you want to be careful to not blow out the highlights to the point of no recovery. I typically start with 1/250 and adjust from there.

Manual Mode

You want full control over your camera when photographing the moon. This means controlling aperture, ISO, shutter, and focus! It seems like you should be able to autofocus on the moon, and sometimes you can, but the dynamic range is often so extreme that your camera could get confused. It’s just easier to give in and go full manual.

Keep It Simple

It’s very easy to forget things in the moment when on location, so a simple rule that can get you close is to match your shutter speed with your ISO. If you shoot 1/250 then use an ISO of 250. If you shoot 1/1000 then use an ISO of 1000. It’s not always perfect, but it gets you in the ballpark where you can properly tweak from there.

Plan!

Buy PhotoPills. If you do any kind of landscape photography consistently, you will want this app. You can easily see when the moon rises, where it’ll be, what time you need to be where, etc (plus it has sunrise/sunset, milky way, AR mapping, and so much more).

Epic shots require a little thought ahead of time, so don’t ignore this step.

And finally, have fun!

Riona Buthello

I grew up drawing and painting almost constantly. These days my “creativity” comes out through photography. But when I stumbled onto Riona Buthello’s work, I’d found something that combined the two.

Riona Buthello

Like, c’mon. Incredible.

Many artists paint things that look like photographs, or attempt to, but there’s something about her style that aligns perfectly with my tastes. I strive to take photos of these exact moments.

From her site:

My work tends to make people stop and wonder whether they’re looking at a photograph or a painting until they see these slight textures in my work.

I would say she nailed it.

I recommend following on Instagram, too, for the video clips — it comes to life so effortlessly.

Infrared Sensor Conversion

I recently had the sensor in my old Fujifilm X-T3 converted to infrared (720nm). I used Kolari Vision for this, which involved sending the camera off for a few weeks. The result is exactly what I had hoped for!

The cheaper alternative is to use lens filters to do this on top of a normal sensor, but they knock out 6-10 stops of light, which means you can forget hand-held shooting. Everything must be stood up on a tripod, which is fine, but kind of limiting. So I opted for the full, permanent conversion.

Infrared Parking Lot

It’s been a blast so far! Seeing the world through infrared opens up a ton of new photographic opportunities. It’s tough dedicating a camera to this permanently, but since that affords me normal shutter speeds and accurate autofocus, I have no regrets.

Oh, and a bonus: infrared photography works best in mid-day, blue sky, full sun situations (which is otherwise a photographer’s nightmare). So now I’m able to shoot 24/7, no excuses!

The Proactive Engineer

I’ve been a young engineer, mentored young engineers, and now I manage (some) young engineers. One of the best perspectives a young engineer—or any engineer—can have is being proactive.

Engineering is complicated. There are often multiple teams and departments coinciding with ever-changing requirements. This can provide engineers with a tempting out (or “pause button”) when working on a problem because they feel blocked on some level. Does something like this sound familiar?

We can’t work on that yet because there’s no API.

Quite often, though, there’s more an engineer can do than claiming blockers. With minimal additional effort, let’s see how much more helpful a response like this might be:

We can’t work on that yet because there’s no API. But I spent some time thinking about what inputs and outputs we would need to properly build X feature, and wrote up my notes as a proposal for the backend team’s consideration.

Makes a difference, right?

It’s easy to throw things over the fence. It takes intentional effort to avoid it. But when you do, that proactive mindset can go a long way within a team and helps nurture a problem solving culture. It’s not about you, me, or them. It’s about solving a problem and helping everyone involved come up with the best outcome for the situation.

We all want to work with helpful, caring individuals… a proactive brain can help convey that to others.