# Benjamin Tumbleson > A collection of thoughts from tech consulting, startups, and everything in between. Public Ghost content for AI and LLM tooling. This file includes a bounded export of public pages first, then recent public posts. Append `.md` to any post or page URL to get the content in Markdown (for example, `/example-post.md`). ## Pages ### About this site URL: https://www.bentumbleson.com/about/ Last updated: 2024-08-30T21:28:59.000Z Benjamin Tumbleson is an independent publication launched in August 2024 by Benjamin Tumbleson. If you subscribe today, you'll get full access to the website as well as email newsletters about new content when it's available. Your subscription makes this site possible, and allows Benjamin Tumbleson to continue to exist. Thank you! ### Access all areas By signing up, you'll get access to the full archive of everything that's been published before and everything that's still to come. Your very own private library. ### Fresh content, delivered Stay up to date with new content sent straight to your inbox! No more worrying about whether you missed something because of a pesky algorithm or news feed. ### Meet people like you Join a community of other subscribers who share the same interests. --- ### Start your own thing Enjoying the experience? Get started for free and set up your very own subscription business using [Ghost](https://ghost.org/?ref=bentumbleson.com), the same platform that powers this website. ## Posts ### On moving to San Francisco, the city of serendipity URL: https://www.bentumbleson.com/on-moving-to-san-francisco-the-city-of-serendipity/ Last updated: 2025-11-25T06:06:50.000Z My wife and I moved to the Bay Area just about two and half years ago. It seems to often come up in conversation that we're from the Midwest. Even when I reconnect with friends and family, there's a familiar question I get a lot. *"How are you liking it so far?"* Good. *"You think you'll stay for a long time?"* It's quite different from the Midwest, and I want to talk about that for anyone that might wonder why anyone moves here, or why they'd stay. But ahead of that, I want to unpack the major takeaway I have living from my time here: ### San Francisco is the city of serendipity. The longer you can afford to stay here and be "in the scene", the more likely you are to meet interesting people, get involved in some project/ startup/ exploration, get advice, get asked for advice, attend events, overhear interesting conversation, find a better role, *build momentum*. ### Don't ignore what I said at the beginning of that takeaway - "*the longer you can afford to stay here*". This is a very personal limitation. --- ![](https://www.bentumbleson.com/content/images/2025/11/PXL_20230629_201448180.jpg) ### **Finding a Quality of Life Balance** When I was a senior in college, I had been tinkering on some stuff that I'd put somewhere between *idea* and *startup*. I had a proof of concept, had good mentors, validated a business case, and done pretty much everything except find and maintain users. I was trying to build something in a really regulated space, and I had somewhat convinced myself that the only way forward was to move to San Francisco, raise money on the concept I had, and use the funds to navigate the regulatory environment, and hire actual developers. I was still pretty new to programming, and without the help of ChatGPT a decade ago, I was moving slower than I would have liked. I ended up going in consulting, somewhat by luck, but that's a story for another day. I did not make it to San Francisco right out of school. When you're 22, right out of undergrad, you have a few things going for you: 1. You have no dependents/ obligations. You don't have a family, you don't have a mortgage, you probably don't have a pet dog/ cat. Maybe you've got a significant other, but that person is still a self-sustaining individual, so you can still operate without too many requirements. 2. Your standard of living is likely quite low. You might have finished college in some crappy off-campus apartment. And you probably ate quite poorly, or at least got pretty good at eating cheaply. You often hear people say *watch out for lifestyle creep*. Very practical advice, but I think it's quite normal, dare I say healthy, for someone to incrementally improve their quality of living once they graduate - notably if your salary allows it, which assumes you've landed a job. My original master plan of moving to San Francisco was a calculated risk, where I figured if I could maintain a "poor college student" lifestyle in SF, I had a runway of at least a good portion of year to figure out how I was going to unlock the next step with my fledging business. Connecting the dots to my earlier point - At 22, my financial standing would have bought me, let's call it a year in "poor college student" conditions. In my 30's, after spending my time in Kansas City in a rented high rise condo, then a home in the suburbs, with a 2 year old Golden Retriever, we had somewhat higher requirements for living to accommodate our lifestyle. Don't get me wrong, we definitely *downgraded* our quality of living moving to the Bay Area, but my belief was that we were dramatically increasing the probability of "succeeding" by being here. So the longer we could tolerate living here, the more chances I'd have at "succeeding" in a way that might make it possible to live here indefinitely. And to be blunt, this mostly means financial stability. --- ![](https://www.bentumbleson.com/content/images/2025/11/PXL_20230816_144503478.jpg) ### The Runway to Succeed In my eyes, the goal of anyone living in a high cost of living area is to make enough money to minimize the burden and stress that housing and their lifestyle represents, while simultaneously increasing your quality of life from living in a nicer home, in a nicer area, with less commute (maybe none at all). Maybe you can afford an au pair or nanny to make it easier to raise kids, or at a minimum get into an afford day care without it being a burden. Maybe you have a house cleaner, a landscape crew, maybe even a private chef that takes grocery shopping, cooking, and cleaning off your plate for some part of the week. Money buys time, no way around it. Money also buys access. This can be really tangible like joining the yacht club, but it's also the groups you're in, and the "happenings" you become privy to. SF is a big collection of loosely formed clubs, organizations, groups, cohorts, etc. Probably true in every city, but it feels much more tactile here. Ironically, having money also makes things cheaper. I don't mean this in the way one might say that it's expensive being poor (which is it to be clear), but more so in the sense that when you're accomplished (reasonably well correlated with financial wellbeing), you aren't paying an entry fee to go to an event. Maybe you're even being asked to speak, maybe you're commanding value for *your* time while others have to pay for the privilege of being there at all. My crude analogy to this point is wealth accumulation is sort of like pushing a snowball up a hill. In San Francisco, the hills are steep and long, but if you make it to the top of the hill, your snowball will keep growing, keep compounding as it rolls down. In the Midwest, literally and figuratively, the hills aren't long, nor steep. You can get your snowball rolling more easily, but you don't have the same opportunity to get momentum. At the crux of this is that the entry point for quality of life is higher in the Bay Area, but you might also argue that the ceiling is equally high. There's year round "jacket weather" (maybe not always good?), you can be within 30 minutes of a beach (although you probably aren't swimming), there's hiking opportunities in most directions, and you can't forget about Napa and Sonoma just 90 min North. Drive a few hours for Lake Tahoe, you can ski at Palisades in the winter. And that's just on the leisure side, the professional gain of being here is implied, right? Think about this heuristic in a lower cost of living area. You may have few chances to really "make it" (however you want to define this), but you might be very comfortable with everything in your life (housing, career, family/ friends, etc). You may never tip the scale. The one dimension I think people somewhat underestimate, particularly when you're young, is if you have the right trajectory in your growth to afford a future lifestyle that you cannot yet imagine or predict. At somewhere between 2-3% inflation, prices will double every \~25 years, even less in high inflation periods like we've seen. So in a \~40 year career, the starting expectation you might have had for where you needed to end up needs financially needs to be *doubled* at a minimum to keep pace. This doesn't even include the lifestyle creep that I hope you'd aspire to between 22 and 62. In the Bay Area, if you make enough money, if you get a windfall from an exit, a tender offer, some other form of wealth building, then you can buy the $3M home in Pacific Heights or the Peninsula, send your kids to private school, and ignore the fact that it is a *grind* to even exist here, let alone succeed. I don't think everyone is as lucky. No one is saving enough money from their job to afford the down payment on that nice home without being crazy levered with laughable debt-to-income ratio. ### My second core takeaway is that you need to find the right balance of optimizing for quality of life, while equally enabling your runway to keep getting a chance to sit at the table that is San Francisco. As you get older, as you accrue obligations in life, to your spouse, your pet, your parents, your relatives, your friends, your career, the debt on your car, your mortgage, etc. So you may not be able to keep living like a 20 year old when you're 30 and beyond. And you don't want to look back and feel like you over-indexed, sacrificing too much of one part of your life for another. This also works the other way around. I think if I had stayed in the Midwest my whole life, I would have always wondered if I had settled, if I had traded some relative comfortable lifestyle with a lower professional ceiling, or at least a slower trajectory. The strange analogy that just popped into my head: imagine you were attending a party. As you were about to walk into the door, a guy offers you money to go home and not attend the party. Suddenly you now have to weight the value (however you want to define that) of the party, versus the value (in this case monetary, but also consider other forms of value like *time*) of the stranger and their offer. The party is a lot more enticing, and you aren't giving up much to be there. As you age, the value proposition changes for a variety of reasons. At what point do you not feel the need to attend the party? --- ![](https://www.bentumbleson.com/content/images/2025/11/PXL_20230917_235116511.jpg) ### The Scale to Stay As a tangent, I think everyone *tolerates* where they live. There is some deeply interwoven mix of practical, emotional, social, and psychological forces that tether you to a place. Some of the most practical forces that come to mind: - Housing/ Cost of Living - Access to stable housing, buy vs rent, quality of housing, risk of displacement, cost of food - Career/ Work - Opportunities, industry concentration, network density, local prestige, commute, earning potential, labor competition - Relationships/ Social Ties - Family, friends, partner, community, neighbors, proximity/ availability of care takers, stage-of-life alignment, ease of meeting new people - Community/ Belonging - Sense of identity, cultural/ lifestyle alignment, language, demographics, "safety net", shared values/ worldview, volunteering, religion - Emotional / Psychological Forces - Fear of change, comfort with routine, attachment to personal history, city-based identity, nostalgia, perception of "special" - Politics/ Governance - Local government, social services, safety/ crime, trust, education system, civic engagement, representation, tax system, infrastructure, land zoning - Physical Environment - Proximity to nature, walkability/ transit, climate, density, natural disaster exposure, parks/ public spaces, water/ mountains, greenery - Inertia/ Friction/ Flywheel Effects - Logistics of moving, career setup, kids in school, career licensing, immigration status, social capital accumulation Some factors are positive, some are negative. Wealth impacts a lot of these. At some point, the scale might lean so far that it doesn't feel like *tolerating* anymore. You might not even be wealthy, but you might have a flywheel from family, friends, your career, etc. that makes it hard to even consider leaving where you're at. ![](https://www.bentumbleson.com/content/images/2025/11/image-2.png) Thinking about these forces as a scale. More green than red? So when you do eventually move, the scale resets. It's not quite equal, as you move elsewhere, you start to optimize for other things, you make tradeoffs, sacrifices, you reassess the weight of each element. --- ![](https://www.bentumbleson.com/content/images/2025/11/PXL_20250706_204132432.jpg) ### The Juxtaposition of Entrepreneurship in SF Living in such an expensive area means that it's very difficult to quit a salaried job to go full time on something unless someone/ something has already backed you, or you're willing to materially cut/ reduce your living expenses. The latter can be particularly difficult if you've built up any level of obligations in life, or lack a runway (*e.g. most people don't quit their job on conviction when living paycheck to paycheck*). The largest accelerators somewhat cater to this idea of judging your commitment based on the conviction to do something increasingly illogical, or give up a cushy lifestyle and the credential of working at Big Tech. So the ultimate irony is that you're asking potential entrepreneurs to take a massive leap of faith to even be there. Or perhaps the Bay Area has conditioned young people to keep their burn rate as low as possible, such that there is little difference when they do quit a salaried position. It's hard to adopt that culture if you didn't start on it, at least be privy to it growing up. As someone who grew up in the Midwest, the "culture" of Silicon Valley was almost fictional. In the HBO series, "Silicon Valley", although the show is largely satire, you'll notice that in the first few episodes, Richard and his friends from the "hacker house" they share *all work at Hooli,* which functionally could be any big tech. Presumably getting paid a salary from Hooli, with a reduced rent for the obligations of their "hacker house", Richard functionally had an unlimited financial runway. Quitting Hooli to work full time on Pied Piper didn't produce any notable change in Richard's living conditions, community, relationships, etc. I can't judge how realistic this is since it doesn't describe me. But I digress. --- ![](https://www.bentumbleson.com/content/images/2025/11/PXL_20250614_201112164.MP.jpg) ### Looking Ahead Living in San Francisco is both a privilege and a strain. A pleasure and burden. Higher highs and lower lows. I'm glad we're here, but I'm not sure it's the endgame for us, although we only have to make that decision a year a time, in sync with our rental agreement. I've somewhat accepted that the “Midwest version of home” probably doesn't exist here, at least not in the same form. The longer I'm here, the more I re-baseline against the norms of the Bay Area. The weather, the ocean, the opportunity. Things I'll miss when we leave. The cost of living, the distance from family... things I won't miss. I'm pretty confident that when I look back at my life, regardless of how long we stay here, I won't (and don't) regret the decision to move here. Although I wonder how I would have adapted moving here right out of college versus in my late 20s, I'm pretty happy with the balance we've achieved. So that's a Midwesterners take on San Francisco, the city of serendipity. --- ### The Tactical Things about San Francisco If you read this wanting even more tactical opinions of what housing, utilities, food, transit, community, leisure, and politics are like here, I'm working on that in parallel and will update here when published. --- *Photos all taken by me across the Bay Area.* ### Do a lot of things URL: https://www.bentumbleson.com/do-a-lot-of-things/ Last updated: 2025-02-01T22:12:28.000Z I think it's easy for people to see that you (or anyone) is often defined by a single part of their life. Generally I see four ways that someone is "classified": - **Career.** For many people, it's their career - some specialty in their craft, programming, data science, marketing, writing, painting, "influencer", whatever. - **Industry.** For others its their industry - AI, clean tech, automotive, airlines, retail. You get the idea. - **Accomplishment.** Fewer exist here, but you might be notable for some accomplishment (potentially related to your career or industry), but it could also be academic, sports inspired, a societal contribution, etc. - **Relationship/ Event**. Even fewer here, but you're "someone" because you're related to someone from the above (think famous people kids, parents, siblings, cousins), or you're known because of some happenstance event (won the lottery, victim of a notable crime, caught Barry Bonds record-breaking homerun ball, etc.). Some people here will own this designation, others will hide it and try to "rebrand" to one of the above. I say it's easy because this is how we're often setup to understand people we don't know. Go read Wikipedia or LinkedIn and within a few seconds, you can probably pin someone to one of the above. --- ### Beyond the obvious Now think of one of your friends. It can be a childhood friend, or maybe even a work friend. You know them from something of the above, but they've got more to them - **Hobbies**. They enjoy doing something in their free time: cooking, hiking, reading, coding, surfing, working out, freelancing, photography, playing a sport, fixing cars, socializing, blogging, playing video games, etc. Some have many hobbies, others have few. - **"Activities"**. Something a little less defined than a hobby, but generally encompasses things you might do regularly. This might be something *you* know someone for even if they don't themselves. Being a full time parent/ childcare, you might enjoy cooking (but it's not a hobby), or you enjoy walking but you're not a runner, nor into fitness. Maybe your office colleagues know you to be a jokester. Lots of loose definitions here. - **Skills.** This is often synonymous with hobbies, as you typically build skills and experience if you do anything long enough. You can have a hobby without skills and vise versa, though that's probably less common. - **Ambitions/ Pursuits.** Everyone is in pursuit of something. Some are tangible, others are a state of mind. Some are strong ambitions that influence hobbies, skill development, career, etc. Others are fleeting. Ambition isn't quite boolean. I tried to quantify it a bit below. ![](https://www.bentumbleson.com/content/images/2024/02/image.png) I don't think any particularly position on the grid is wrong, it just attempts to add some structure to how I think people are motivated – although to be clear I'm not making a stance on motivation, just that ambition takes many shapes. What would it look like to move around this grid? --- ### Do a lot of things, even if you think you might be known for just one My takeaway here is people you idolize, and look up to because they are at some place in their career, or a figure in their industry, but under the surface, those types of people often have more "happening" than you can see. > This relates a bit to the idea of "building a brand", but I find that the people that excel in one thing, often excel in others. Or that they didn't get to where they were today without some healthy obsession towards tangential things. I'd also argue the more "things" you do, the more likelihood you have succeeding in any one of them. Partially because many skills are correlated, making it easier to do the next thing. And partially because the more times you "spin the wheel" of life, the better odds you have of stumbling onto something you're really good at, or get an immense amount of fulfilment from. Maybe this is why more than 80% of college students end up switching majors at least once [\[1\]](#fn1). So yeah, do a lot of things. You never know what might come of it. --- 1. [https://utulsa.edu/news/normalizing-the-norm-of-changing-college-majors/](https://utulsa.edu/news/normalizing-the-norm-of-changing-college-majors/?ref=bentumbleson.com) [↩︎](#fnref1) ### Initial Impressions of Figma Slides (beta) URL: https://www.bentumbleson.com/observations-from-figma-slides-beta/ Last updated: 2025-02-01T22:01:09.000Z I spent some time this weekend exploring Figma's new Slides offering. This is in no way comprehensive, but I wanted to put my thoughts on paper as someone that feels uniquely qualified to talk about building presentations. I also would acknowledge that everyone is going to have a different opinion on who the target audience for this tool is, and I am not a designer and my day job isn't in Figma, so I'm approaching this from the perspective of viewing Figma Slides against PowerPoint or Google Slides. I've got a bit more of a point of view in my **Threats** section below. ### Why should you consider my opinion? I like to think that I've spent more time around presentations and slides than most. I started my career in consulting - the "business" of slides in many cases. I'm no stranger to creating presentations as deliverables, making materials to win 7-figure deals, and presenting in front of C-suite audiences. Many of my coworkers would refer to me as an "expert" in slides, or more particularly crafting a narrative and conveying it well in presentations. I spent my consulting career in the Microsoft PowerPoint ecosystem, and the last many years in the startup community working with Google Slides. While the target audience of Figma Slides may be focused on designers and perhaps product managers, slides themselves are often the most frequent manifestation of a companies brand and how they communicate (internally and externally), so it stands to reason to me that Figma Slides would be viewed optimistically as a replacement to the common tools of today. ![](https://lh7-us.googleusercontent.com/slidesz/AGV_vUcBR5EFk7_ZRBwZPZQ3NFN7-ny4S5yDkMqTtVWwuMu-ciFn0ohtAFXeACzi_LprJ8lh9IxBQcnBdzbqMtI_2EJSSSuZNIfGJxF47cV3cepRdPcgg2MjSB951NNFoOgSUDZraabxbggKUJtqDgDviz61Trn_jeI=s2048?key=BcJim7m1VM4ZZI4rygBDRw) Heard around Slack... If you're involved with Figma Slides, I'd be happy to share more and elaborate on anything as needed. `ben @ this website .com` ### Caveat I do not have a "Design plan", so no part of my review here is based on those capabilities. I'm also approaching this from the lens of a "general purpose" presentation tool (*see more below in my "Threats" section),* which may not reflect the direction Figma is looking to take the tool. --- ## Strengths: - Love the "hierarchy" of slides design. Being able to create naturally nested sections is great and acknowledges how most narratives are told (in sections). I'd venture that very few people in PowerPoint/ Google Slides ever switch to the "grid" view. Figma found a great way to make this a meaningful part of the design process. On top of this, you can actually edit elements of slides directly from this birds eye view. Truly powerful! - `Present + Notes` appears well executed and I can see the value particularly if presenting physically. Might also work well for predictability on a Zoom call. - I really like the idea of named "assets" within a presentation. Google Slides and PowerPoint have layers pretty well covered, but there's never a differentiation. I could see this going a long way when you have complex slide designs that have a lot of related elements. Every presentation tool has the concept of groups, but this feels uniquely powerful. ![](https://www.bentumbleson.com/content/images/2024/07/Screenshot-2024-06-30-at-5.32.10-PM.png) Asset-based elements. Nifty! - As I'd expect from a product derived from the core Figma platform, even without the capabilities in the "Design" mode, the interaction and formatting between elements is powerful. For example, something as simple as changing the padding between two elements is no longer an exercise of moving each element and eyeballing gaps - you can actually just modify the gap itself. Simple, smart. ![](https://www.bentumbleson.com/content/images/2024/07/Screenshot-2024-07-02-at-8.16.42-AM.png) It's the little things isn't it? - Very cool that when you zoom in, eventually you get to the "pixel" level. Really helps with "pixel perfect" alignment - something in Google Slides and PowerPoint that doesn't really exist. You just nudge things along, and in many cases, you might even draw your own "grid" lines. ![](https://www.bentumbleson.com/content/images/2024/07/Screenshot-2024-06-30-at-5.43.04-PM.png) Pixel perfect on zoom. - The "line" tool is deceivingly powerful. Once you create a straight line, you can double-click into the line to create additional points on the line that can be moved or converted into smooth bends. Only caveat from my own testing is it looks like once you "convert" a line into a fluid line (vs straight), you lose the ability to define new points on the line. This may be possible via a keyboard shortcut, but I haven't found it. Notwithstanding, this is a powerful feature for some of the more "custom" line work that occasionally is an important part of a slide. - Native import of SVG's is hugely appreciated - particularly those coming from Google Slides where this isn't supported. - The Quick Action menu is awesome. Reminds me a lot of VSCode, or navigating in Slack. Once I discovered this, I found myself trying to find hidden (or nested) options. - I'd be remiss if I didn't appreciate the aesthetics of this tool. It's beautiful. --- ## Weaknessness: *Many of these are minor, don't take this as afront to the product as a whole.* - Shapes section appears very limited. No chevrons which is a pretty common shape used in timelines, nor is here anything related to flow charts. The arrows selection is a bit surface deep, although I understand that you can theoretically create any type of line using the capabilities hidden within the last two options. I would have liked to see more influence from FigJam here where creating lines between elements is exceptionally seamless. To put this another way, I think what FigJam gets so well is it makes reasonable assumptions about how you want to use a shape, and how you want to "link" them with lines or arrows. ![](https://www.bentumbleson.com/content/images/2024/07/Screenshot-2024-06-30-at-5.36.31-PM.png) Shapes dialogue Maybe Figma Slides can lift the shapes implementation from FigJam? ![](https://www.bentumbleson.com/content/images/2024/07/Screenshot-2024-07-02-at-8.57.30-AM.png) FigJam shapes (basic, flowchart, misc) - On the topic of shapes, the built in shapes like Heroicons from Tailwind is awesome, although it doesn't look like you can modify the shapes outside of size (could there be an "explode" option?), and the contextual menu appears to not match what the "shape" is. For example, including an icon shows a slider for "corner rounding" which only applies to a border on the icon, yet a border isn't included by default. Should that menu be conditional and only render *once* a border is enabled? A visual cue in the right sidebar on what "layer" in the shape you have selected might be useful, since the default after inserting an icon (in this use case at least), relates to the *border* rather than the icon. - "Font size" should probably be included "above the fold" ("show details"). As I've venture this is one of the most common options. The real-time hover-to-see change of font size is really nifty, although I wish there was a bit more inspiration from core Figma that used a sliding scale so you could move sizing freely. Using non-descript font sizes in standard increments looses a bit of its value when the canvas itself has no ruler or sizing. - There doesn't appear to be even basic "master" or "layout" slide types. "Templating" only seems to cover background color, font, and text colors, although it looks like you can allow entire slides to be treated as templated slides. It's also (reasonably) [gated behind organization or enterprise plans](https://help.figma.com/hc/en-us/articles/24290529512471-Create-and-publish-custom-slide-deck-templates?ref=bentumbleson.com), which doesn't bother me. Perhaps this is purposeful, I understand the concept of layouts and masters can often be overkill for creating presentations, but then this is also juxtaposed to a presentation tool built from a design platform like Figma. Maybe there's a simpler implementation here? I would like to see core elements of slides locked that create a more rigid structure for adoption and help maintain brand standards. ![](https://www.bentumbleson.com/content/images/2024/07/Screenshot-2024-07-02-at-9.12.36-AM.png) Basic implementation of layout and master concept with slides. - Why is presenting in a new tab by default? This could be interesting if you wanted to preview something in a different window, but unlike coding, most slide building is WYSIWYG which defeats the purpose of "hot reloading" which theoretically you could do with this setup. - Exported slides appear to include "rounding" on the frame. This is notable if you have a non-white background, you end up with a bit of rounded-ness. Just a bug, hopefully an easy fix. ![](https://www.bentumbleson.com/content/images/2024/07/Screenshot-2024-07-02-at-9.36.13-AM.png) Exported full color backgrounds have the background rounded. Bug? - I haven't been able to find an option to mark slides as "hidden" - useful for both presenting and exporting. Every meaningful presentation I've ever made or been a part of has had a a series of "draft" or different versions of slides. I suppose I could move all the slides I'd prefer to be hidden into their own swimlane, but then I have to move the slides out of the order they might actually appear it. It also makes exporting exceptionally cumbersome since I need to physically delete the slides I don't want in my PDF, then export, then "undo" the delete. - No charts/ graph generation tools. Not much to say here, just something that you might expect, even a basic implementation. I have more thoughts here in the **Opportunities** section. Overall, I don't think this is a huge issue because I think most "above average" instances of data visualization happen in a fit-for-purpose tool like Looker or Tableau, so you're really just talking about inserting screenshots. - How is the layout grid defined? This is the default below. To my eye, this is too much padding in both directions. It doesn't appear customizable. Also this visual style of grids, while explicit in terms of knowing where you are, particularly if you're zoomed in, because of the color, it may hinder your ability to "visualize" a finished slide with the deep red overlay. I would venture most usage of this feature would be quickly toggling it, which I think is probably the wrong metric to be tracking. ![](https://www.bentumbleson.com/content/images/2024/07/Screenshot-2024-06-30-at-5.44.24-PM.png) Default layout grid. Seems... aggressive? - Pasting images seems awkward currently. As a caveat, I'm using Firefox in my testing, but for whatever reason, when you paste an image, you first get a mini grayed out "Paste" in a menu that renders right where your cursor was. After a half second or so, `Paste` becomes clickable. *Then* your image is inserted. Feels like more steps than necessary. ![](https://www.bentumbleson.com/content/images/2024/07/Screenshot-2024-07-02-at-10.04.21-AM.png) Example of my "paste" menu which rendered a bit awkwardly. - The "Selection Colors" part of the sidebar doesn't seem to have any context around where the colors come from. For example, I created a shape and changed the color of the text in the middle to white and green. The "selection color" accurately shows the three colors in play, but has no context as to what part of the shape they are responsible for. This is perhaps a bad example, because I would have liked to see basic direction that identify *what* colors are attached to. In this case I can visually see the purple is the background, but you can imagine a much more detailed shape, you might be playing a game of guess-and-check to get to the color you're looking for. `Border` has its own section in the sidebar but `Fill` doesn't? How about `Text`? ![](https://www.bentumbleson.com/content/images/2024/07/Screenshot-2024-06-30-at-6.43.22-PM.png) Selection colors could become confusing. In this example with a default table, there are 4 colors reflected. In this case, the black *probably* refers to the text color, but as there is no text, I can't verify this visually. And the last two colors seem strongly like they could both be the gridlines. Seems like there should be contextual sidebar menus that cover tables with `Grid`, `Fill`, `Text`, and maybe even `Cell`. ![](https://www.bentumbleson.com/content/images/2024/07/Screenshot-2024-06-30-at-6.44.56-PM.png) - On the topic of tables, there appears to be no ability to "merge" (or split) cells. This is particularly meaningful since there also isn't an ability to change the color of individual lines of a cell. For example, you can't individually set the "bottom" of a row to a different color or thickness. - Tables appear to have rounded corners in all cases. While I generally agree with this in most cases, I think it's an oversight to not make this a toggle, or a slider like you see when using shapes. --- ## Opportunities: *No particularly order, just a few things that came to mind.* ### Slide Nesting Related to my first strength of presentation layouts, it feels like there's more opportunity here related to nesting. This is a bit derivative of how I'm building out slide hierarchy with [SlideCub](https://slidecub.com/?ref=bentumbleson.com), but the idea (in my mind) is that every slide can either be a parent, child, or sibling. The advantage of this approach is you remove the concept of an "appendix". Everything is either its own section, a sub-section, or a deep-dive of a previous slide. ![](https://www.bentumbleson.com/content/images/2024/07/Screenshot-2024-07-01-at-7.41.27-AM.png) Example of slide nesting concept This allows you to create levels of granularity really naturally. Pitching to your engineering team? They'll want to see all the details. Showing this to your C-Suite? Probably skip those details the first time around, but it's still the same deck. ### Cursor Chat while Presenting Particularly in virtual contexts, when there is an "internal" vs "external" audience, it's very common (in my experience) to have the team presenting create a small Slack chat off to the side. I would love to see "cursor chat" still active on the "notes" slide while presenting. Better than keeping a slack chat open to have an internal discussion! ![](https://www.bentumbleson.com/content/images/2024/07/Screenshot-2024-07-01-at-7.56.18-AM.png) Fictional, but could facilitate internal, contextualized discussion mid-presentation. ### FigJam integration? I was hopeful that the "live interaction" tab would allow me to integrate FigJam diagrams, but alas. Almost every FigJam diagram I have worked on related to sales, marketing, or product is inevitably screenshotted or exported to PNG and copied into a slide deck. The flaw of this approach is the someone will end up pinning a comment wanting a change on the diagram which they don't have access to (since it's simply an image at this point), and then someone will need to jump back into FigJam to update the diagram. Merging the "whiteboard" of a FigJam board could be huge, in the same way it looks like integrating prototypes is a core feature. I'd need to do more thinking if you want to just recreate some of the FigJam tools directly inside the Slides tool, or if you just want a seamless integration where a diagram stays in sync to a slide, maybe even going as far as to make sure its properly scaled to fit in a container as well as make sure the font size is readable. ### Figma "Analysis" - data visualization Building off the weakness of not having any ability to generate basic charts (think bar chart, line chart, pie chart, etc.)... it feels like this an opportunity that exists somewhere between Figma Slides and FigJam. "Figma Analysis"? We need something that converts raw text into clean diagrams/ charts. [RawGraphs](https://www.rawgraphs.io/?ref=bentumbleson.com) is an excellent open source implementation of this idea. Might also be a natural extension of the "live analytics" getting built. For example, if you take a big survey or poll throughout a presentation, you might want to slice/ dice that data, especially if you could extract metadata about that user or action (e.g. department, seniority/ title). A minimal implementation of this might be a "smart" import from tools like Looker and Tableau. Since I'd generally consider Figma Slides to be the best equipped to work with raw vector shapes, there's some interesting opportunities to do SVG exports from those tools directly into Figma Slides. Little bit of AI and you could "hook into" screenshots and do things like rewrite labels or change the font size to fit the medium that is a slide. ### Smaller things - Some of the actions you take in the UI trigger a mini notification in the bottom (awesome), but others that are async in nature, don't trigger a notification so it's not always obvious that the command worked - particularly when you do it via a keyboard shortcut. For example, `Copy as PNG` triggers a small notification, but `Copy Properties` does not. - The "shuffle" styles is an interesting idea, but it feels like this is only useful once. Once you have a style "picked", you probably aren't changing color palettes dramatically between slides. I'd also venture that any deck based on a corporate template would already have colors defined, so you might not even want to shuffle. - Too many useful options from "Object" are tucked into the "Figma" menu rather than contextually available in the right side bar after selecting a slide - particularly the `Bring to Front` and `Send to Back` options. I'd love to see a smaller menu in the right hand size bar with these organizational options. Particularly if there is "extra" space above the canvas, you could even bring a horizontal contextual menu in play. - The "Comment" icon on the bottom is the only action that seems to trigger its own unique "sidebar" menu. For what it's worth, this is a great menu, but I would argue addressing comments is more important than animation, so why does animation get its own tab next to "Design"? This feels like a different flow than the rest of the UI. - "Shapes" almost certainly needs a default keyboard shortcut. If you borrow the nomenclature from Google slides, *everything* on a slide is either a shape, image, or table, so I'd like to be able to jump straight to that. Interesting *some* of the shapes themselves have keyboard shortcuts, so maybe this is already solved? It seems to me we might want to remind a user that all of the shapes (lines, rectangle, ellipse, etc.) are indeed derivative of a base "shape" class - this would provide familiarity with menus and related options for styling. --- ## Threats: ![](https://www.bentumbleson.com/content/images/2024/07/presentationToolAssessment.png) Strictly my opinions here, folks. I suppose you really have to define competitors here before we can tangibly think about "threats". My belief that Figma saw success in growing their market beyond designers with FigJam, and may wish to lean into that more as they build out a more comprehensive suite of "work management" tools. If true, this puts this Figma Slides moreso on the track of a "generalist" set of presentation tooling. Both Google and Microsoft (representing Google Slides and PowerPoint respectively) would largely be considered "matured" products at this point in time, in that the remaining investment you see from both of them is on the fringes. The past many years has been Microsoft catching up with Google on collaboration - something Google historically dominated. There's some interesting stuff coming from both parties in the space of AI and helping generating content (whether that be text or actual layouts), but I haven't seen anything meaningfully deployed into production. This is where smaller players, like Gamma, Tome, and Pitch to a lesser degree are leaning into a "AI-first" approach. Canva is a bit of an outlier, similar to Figma in many ways that they were able to build atop their existing platform. Canva doubles down into their template and "library" system, which is less so from Figma. Prezi is the last major player I'd list. Still very niche - the main reason one would choose Prezi is simply the engagement that comes from macro zooming in and out of a massive canvas. So the main threat I see is that if Figma is trying to be mass market, they have a "feature parity" gap to close. If they double down into AI as a differentiator, they're putting themselves in a ring amongst many small (albeit well funded competitors). Seems to me that Figma's strength is leaning into its design roots in pursuit of a mass-market tool. This is where, I believe, there has been the *least* amount of innovation, in terms of how content is organized on a page, aligned to a grid, narratives are cohesively built, etc. --- ## Conclusion I'm excited. Although Figma isn't a tool I work in every day, I've become accustomed to the robustness and power of the platform as a design tool. FigJam was a brilliant addition to the portfolio, and I see Figma moving in the right direction building their own ecosystem of tools that empower *builders*. ### Experiments with the Google Slides API to recreate slides URL: https://www.bentumbleson.com/experiments-with-the-google-slides-api-to-recreate-slides/ Last updated: 2024-03-24T06:39:48.000Z ![](https://www.bentumbleson.com/content/images/2024/03/image-23.png) Thanks Dall-E for this "art". I love the predictability of web apps. All of the interaction between the client and the server is predictable, observable, and often easy to recreate (curl, etc.). I also really like presentations, Slides, PowerPoint - pick your poison. I know Amazon would rather read a memo, but there's something about crafting a narrative in a set of slides that is really powerful. Anyway. I've been using the Google Slides API for awhile now. Why? Check Part 3\. Today I want to take you through some exploration of the [Google Slides API](https://developers.google.com/slides/api/reference/rest?ref=bentumbleson.com). **TL;DR of shortcomings I've seen:** - The `GET` schemas for presentations and pages cannot be reused, as you might expect to natively be able to "create" slides. - The `presentations.create` endpoint only creates blank slides. - You cannot change the page size via the API. This effectively means every programmatically generated slide is 16:9 (10" x 5.63") - The only way to insert content onto slides is manually crafting a series of `batchUpdate` requests, which are often not 1:1 with their `GET` representations (meaning you must manually translate between these two schema expectations). - Custom shapes are not defined when reading in a `GET` request, nor can they be meaingfully inserted with a `batchUpdate` request. This effectively means you can't programmatically insert icons, logos, etc unless it's a "standard" shape. - Text `pageElements` are returned with excess page breaks, which if directly translated into a `batchUpdate` produce undesired results. Although you can fix this in your translation layer, it certaintly feels like a bug. The takeaway? As of today, I would say the coverage of being able to reliably recreate slides is slightly too low to develop meaningful solutions around it. --- ### Part 0: Intro to GSlides API The API has two main constructs: ***presentations*** and ***pages***. **Presentations**are exactly what they sound like. A simple JSON schema that effectively defines a collection of **pages**, with some metadata like the presentation ID, title, etc. ```json { "presentationId": string, "pageSize": { object (Size) }, "slides": [ { object (Page) } ], "title": string, "masters": [ { object (Page) } ], "layouts": [ { object (Page) } ], "locale": string, "revisionId": string, "notesMaster": { object (Page) } } ``` [GSlides Presentation JSON Schema](https://developers.google.com/slides/api/reference/rest/v1/presentations?ref=bentumbleson.com#resource:-presentation) **Pages** define each slide. There's some similar meta detail in the form of *Properties*, but the bulk of what is actually on the slide is `pageElements` . We'll talk about this element quite a bit later. When you're working with what I'll call "normal" slides, you mostly expect to see `pageProperties` and `slideProperties`. ```json { "objectId": string, "pageType": enum (PageType), "pageElements": [ { object (PageElement) } ], "revisionId": string, "pageProperties": { object (PageProperties) }, // Union field properties can be only one of the following: "slideProperties": { object (SlideProperties) }, "layoutProperties": { object (LayoutProperties) }, "notesProperties": { object (NotesProperties) }, "masterProperties": { object (MasterProperties) } // End of list of possible types for union field properties. } ``` [GSlides Pages JSON Schema](https://developers.google.com/slides/api/reference/rest/v1/presentations.pages?ref=bentumbleson.com#Page) `pageProperties` *only* defines the background fill/ color. `slideProperties` defines the layout and master slide that the current slide is based on. It also defines what your *notes page* looks like, namely how the printed page would be rendered or exporting with speaker notes. In 99% of slides, you get two `pageElements` that define placeholders for the slide image and any text. It's not obvious how this changes given the options for notes pages are defined in real time during the print preview process. This setting also is global, rather than per slide, so I remain a bit confused on the value in defining this for every slide. ![](https://www.bentumbleson.com/content/images/2024/03/image-1.png) Global notes settings, so why do they exist at Page level in API? There's a lot of other detail here about all of the elements that exist within a page, but that's for another day. --- ### Part 1: Creating Slides the Easy Way? I'll start with the biggest limitation of the API from my perspective: There's `presentation.get` response right now cannot be used anywhere. This doubly means that the `presentation.create` method can't do anything except create a blank slide. Let me explain. Everything I discussed above in Part 0, which is effectively the structure of how Presentations and Pages are represented in JSON when you `GET` either a presentation or `presentation.page`, **cannot be used anywhere**. The schema for creating or modifying slides does not match the schema for how presentations and pages are represented programmatically. There are many similar parts, but you can't simply pass the response from `presentation.get` to `presentation.create`. This isn't immediately obvious, because if you, like me, go read the `presentations.create` method, here's the request body schema: ```JSON { "presentationId": string, "pageSize": { object (Size) }, "slides": [ { object (Page) } ], "title": string, "masters": [ { object (Page) } ], "layouts": [ { object (Page) } ], "locale": string, "revisionId": string, "notesMaster": { object (Page) } } ``` [Presentations.create JSON Schema](https://developers.google.com/slides/api/reference/rest/v1/presentations/create?ref=bentumbleson.com#request-body) Looks familiar doesn't it? Really makes it seem like you can/ should include the slides you want created. Don't miss this very important sentence: ![](https://www.bentumbleson.com/content/images/2024/03/image.png) According to Google, this is [intended](https://issuetracker.google.com/issues/74602138?ref=bentumbleson.com#comment10). My first thought was when they rolled out the API in [May of 2016](https://developers.google.com/slides/docs/release-notes?ref=bentumbleson.com), they hadn't implemented the rest of the `create` functionality yet, so the method was really more of a "*create default blank slide*". In 2018 (from the bug ticket), it took 4 years for Google to point out this was expected behavior. I won't hold my breathe this is changing any time soon. --- ## **Part 2: Creating Slides the Manual Way** Since we've learned above we can't actually use the `presentations.create` method to create slides with content, we'll need to rely on the `batchUpdate` method, which acts more as a transactional set of actions to take. Let's go through a few experiments so you can get a feel for how this works and the limitations. ### Experiment 1: Recreate a slide with a circle Via the webUI, in a brand new presentation, let's create a circle: ![](https://www.bentumbleson.com/content/images/2024/03/image-3.png) Creating a circle. That is the only "action" I took, so I can see the resulting `save` request in the network console: ![](https://www.bentumbleson.com/content/images/2024/03/image-19.png) "Save" request from within Google Slides Here's an example of the `bundles` from a similar request also creating a circle: ```JSON [{"commands":[[4,[[3,"g2c5e29abb69_0_0",8,[0.6825,0,0,0.6825,191903,14229],[15,[null,4],19,[null,3],22,381],"p"],[17,"g2c5e29abb69_0_0",null,0,1,[],[12,2]]]]],"sid":"17fb4e261b6935cf","reqId":1}] // Prettier... [ { "commands": [ [ 4, [ [ 3, "g2c5e29abb69_0_0", 8, [0.6825, 0, 0, 0.6825, 191903, 14229], [15, [null, 4], 19, [null, 3], 22, 381], "p" ], [17, "g2c5e29abb69_0_0", null, 0, 1, [], [12, 2]] ] ] ], "sid": "17fb4e261b6935cf", "reqId": 1 } ] ``` Form Data - `bundles` , inserting a circle into a Slide This minified version of what is being saved is certainly cryptic. Let's park this for now, I'll come back to it in a second. ... There's some other stuff in the query string parameters, but my hunch after testing this for quite some time, is that these parameters are not related to the action I just took, but rather just some meta data about the slide, my permissions, etc. Here's my best guess: ```JSON id: 1C4SjQ1EVPtMl9x5qkNtZUtSTzaegJy8JTk6R_BR5GyE, // presentation ID, can verify in URL sid: 17fb4e261b6935cf, // session identifier? vc: 1, // version? Perhaps used if a file is copied/ duplicated c: 1, // collaboration flag? Unclear when this would be false w: 1, // write flag? Assume this is related to permissions flr: 0, smv: 9, // state model version? Pure guess, seems like this is always 9 recently smb: [9, ], // array of possible versions? Maybe used for phased rollouts? token: AC4w5Vi6jWwLUI-uRlxxTzYPnsVIkcUIsA:1711227000756, // some auth token for the API with an appended UNIX timestamp? (although it'd be off by a few days) includes_info_params: 1, // feature flag for more meta data? usp: slides_web, // user service parameter? Basically where the request originates from; I don't see this key>value all the time. cros_files: false, // flag related to compatibility across devices? ``` Query String Parameters from save request via web ... Alright, let's go ahead and `presentations.get` that slide above via the API. I'll skip any detail related to setting up an application and granting permissions as I imagine this is the same for usage of any Google API. Anyway, with the resulting response, I used a debug helper make the JSON a bit easier to navigate, and I went ahead and expanded everything under `pageElements` as this is where the circle is actually getting defined. ```JSON array:5 [▼ "objectId" => "p" "pageElements" => array:1 [▼ 0 => array:4 [▼ "objectId" => "g2c5e29abb69_0_0" "size" => array:2 [▼ "width" => array:2 [▼ "magnitude" => 3000000 "unit" => "EMU" ] "height" => array:2 [▼ "magnitude" => 3000000 "unit" => "EMU" ] ] "transform" => array:5 [▼ "scaleX" => 0.6825 "scaleY" => 0.6825 "translateX" => 4797575 "translateY" => 355725 "unit" => "EMU" ] "shape" => array:2 [▼ "shapeType" => "ELLIPSE" "shapeProperties" => array:5 [▼ "shapeBackgroundFill" => array:1 [▼ "solidFill" => array:2 [▼ "color" => array:1 [▼ "themeColor" => "LIGHT2" ] "alpha" => 1 ] ] "outline" => array:3 [▼ "outlineFill" => array:1 [▼ "solidFill" => array:2 [▼ "color" => array:1 [▼ "themeColor" => "DARK2" ] "alpha" => 1 ] ] "weight" => array:2 [▼ "magnitude" => 9525 "unit" => "EMU" ] "dashStyle" => "SOLID" ] "shadow" => array:8 [▼ "type" => "OUTER" "transform" => array:3 [▼ "scaleX" => 1 "scaleY" => 1 "unit" => "EMU" ] "alignment" => "BOTTOM_LEFT" "blurRadius" => array:1 [▼ "unit" => "EMU" ] "color" => array:1 [▼ "rgbColor" => [] ] "alpha" => 1 "rotateWithShape" => false "propertyState" => "NOT_RENDERED" ] "contentAlignment" => "MIDDLE" "autofit" => array:2 [▼ "autofitType" => "NONE" "fontScale" => 1 ] ] ] ] ] "slideProperties" => array:3 [▶] "revisionId" => "uVl5gEdcOv8lvg" "pageProperties" => array:1 [▶] ] ``` JSON Representation of the slide with the circle via `get` API If you have keen eyes, you're probably starting to see a correlation between the obfuscated `save` request via the frontend, and how the shape is represented via the `get` endpoint. My best guess is most of the integers are enums to actual keys (e.g. `4` \= `CreateShapeRequest`?), wherein the nested arrays are the values from those keys. For example: `[0.6825, 0, 0, 0.6825, 191903, 14229]` This line to me is almost certainly related to `scale`, `transform`, etc. The order and the magnitude of the last two values are what confuses me the most, so this is an effort for another day. Okay, back to the point: Like I mentioned above, there's nothing natively I can do with the `presentation.get` response right now. I can't simply pass it back to a different endpoint to generate this slide, nor will Google accept this schema in any other endpoint. Guess it's on me to do this myself; I need to convert the `pageElements` into `batchUpdate` *requests*. ```PHP foreach ($slide['pageElements'] as $pageElement) { // determine what "kind" of element this is. foreach($pageElement as $elementKind => $options) { // loop through the elements until we find one that can identify the group if(in_array($elementKind,self::elementKinds)) { $this->parsePageElementToAction($elementKind,$pageElement); } } } ``` Snippet of handling this in PHP Here's where things get messy. In this case, the circle is of type `shape`, so now I need to generate requests for anything that someone could do with a shape: ```PHP // ... case 'shape': $this->CreateShapeRequest($pageElement); $this->UpdateShapePropertiesRequest($pageElement); $this->InsertTextRequest($pageElement); $this->UpdateTextStyleRequest($pageElement); $this->UpdateParagraphStyleRequest($pageElement); $this->CreateParagraphBulletsRequest($pageElement); break; ``` In my case, I only expect that I'll need the first two: `CreateShapeRequest` and `UpdateShapePropertiesRequest` as there's no text within the shape. There's some logic behind the scenes I've written here, so for the sake of this study, you can assume that all the translation is being done manually (since we know there's no native way to build up these `batchUpdate` requests. Taking that slide above and passing it through my conversion layer, here's the generated requests: ```JSON { "requests": [ { "createShape": { "objectId": "g2c5e29abb69_0_0bMiydqLYBn0", // origal + random 10 chars so its unique "elementProperties": { "pageObjectId": "SLIDES_API1926000256_0", // page ID I am inserting into "size": { "width": { "magnitude": 3000000, "unit": "EMU" }, "height": { "magnitude": 3000000, "unit": "EMU" } }, "transform": { "scaleX": 0.6825, "scaleY": 0.6825, "translateX": 4797575, "translateY": 355725, "unit": "EMU" } }, "shapeType": "ELLIPSE" } }, { "updateShapeProperties": { "objectId": "g2c5e29abb69_0_0bMiydqLYBn0", // must match the ID of the shape I created in the previous request "shapeProperties": { "shapeBackgroundFill": { "solidFill": { "color": { "themeColor": "LIGHT2" }, "alpha": 1 } }, "outline": { "outlineFill": { "solidFill": { "color": { "themeColor": "DARK2" }, "alpha": 1 } }, "weight": { "magnitude": 9525, "unit": "EMU" }, "dashStyle": "SOLID" }, "shadow": { "transform": { "scaleX": 1, "scaleY": 1, "unit": "EMU" }, "alignment": "BOTTOM_LEFT", "blurRadius": { "unit": "EMU" }, "color": { "rgbColor": {} }, "alpha": 1, "propertyState": "NOT_RENDERED" }, "contentAlignment": "MIDDLE", "autofit": {} }, "fields": "shapeBackgroundFill.solidFill.color.themeColor,shapeBackgroundFill.solidFill.alpha,outline.outlineFill.solidFill.color.themeColor,outline.outlineFill.solidFill.alpha,outline.weight.magnitude,outline.weight.unit,outline.dashStyle,shadow.transform.scaleX,shadow.transform.scaleY,shadow.transform.unit,shadow.alignment,shadow.blurRadius.unit,shadow.alpha,shadow.propertyState,contentAlignment" } } ] } ``` Generated `batchUpdate` requests. To clarify, there is no API, library, etc that tells you how to do this. I had to go through the process of studying the `GET` schema to see how the the slide is represented in JSON, *then* I had to logically convert that into the \~47 request types the Slides API defines as a part of a `batchUpdate` request. So if you're studying the difference between these two, you can see that I'm effectively mapping all of the elements I see in the `GET` response into the `batchUpdate` request schema. And one `pageElement`, such as a shape with text, might result in 5+ requests for the shape, properties, text, style, paragraph, bullet points, etc. > **Sidenote**: Of the 47 request types, 10 of them are related to tables given how much detail can exist between rows and columns and the resulting formatting. Inserting a table itself could be hundreds of requests to get the formatting and text right! Let's pass those two requests back to the API to generate a new slide. First, I'll use the `presentation.create` endpoint to create a blank presentation. Then I'll use `presentation.batchUpdate` with the above payload, and: ![](https://www.bentumbleson.com/content/images/2024/03/image-4.png) Generated slide! Turns out when you create a brand new presentation (`presentations.create`), since you can't pass any sort of template or master to it, you will *always* get these placeholders on the first slide. A workaround would be to create a new slide (via `CreateSlideRequest`) within the newly created presentation, then delete the initial slide with the placeholders on them. The ID of the first slide in any completely new presentation is always `p`, so you could reliably pass the `DeleteObjectRequest` alongside the `CreateSlideRequest` to make this reasonably seamless. Although there's some about needing to do 3 requests to get a truly blank presentation with one blank slide that feels silly. Generally speaking, here's what we just did: ![](https://www.bentumbleson.com/content/images/2024/03/image-6.png) But hey, our gray circle is there! So that's a win. --- ### Experiment 2: Simple Text Let's repeat the above exercise and see where we land, this time using a text element instead of a shape. Here's the input slide: ![](https://www.bentumbleson.com/content/images/2024/03/image-7.png) And the relevant snippet of the `GET` request (note that I'm using `GET` interchangeably with the `presentations.get` method): ```JSON ... "shape": { "shapeType": "TEXT_BOX", "text": { "textElements": [ { "endIndex": 86, "paragraphMarker": { "style": { "lineSpacing": 100, "alignment": "START", "indentStart": { "unit": "PT" }, "indentEnd": { "unit": "PT" }, "spaceAbove": { "unit": "PT" }, "spaceBelow": { "unit": "PT" }, "indentFirstLine": { "unit": "PT" }, "direction": "LEFT_TO_RIGHT", "spacingMode": "COLLAPSE_LISTS" } } }, { "endIndex": 86, "textRun": { "content": "This is a simple text box. There is no additional line breaks, styling, or otherwise.\n", "style": { "backgroundColor": [], "foregroundColor": { "opaqueColor": { "themeColor": "DARK2" } }, "bold": false, "italic": false, "fontFamily": "Arial", "fontSize": { "magnitude": 18, "unit": "PT" }, "baselineOffset": "NONE", "smallCaps": false, "strikethrough": false, "underline": false, "weightedFontFamily": { "fontFamily": "Arial", "weight": 400 } } } } ] }, "shapeProperties": { ... }, "outline": { ... }, "shadow": { ... }, "contentAlignment": "TOP", "autofit": { "autofitType": "NONE", "fontScale": 1 } } } ``` Snippet of JSON "get" representing this slide First note here is the text still exists within a *shape,* although in this its a `TEXT_BOX` not an `ELLIPSE` like in our circle example. ... Let's look at the simplified generated request, below. Main takeaways: - Recreating this single textbox, with one identical (styling-wise) line of text requires 5 different requests: `createShape`, `updateShapeProperties`, `insertText`, `updateTextStyle`, `updateParagraphStyle`. - The `insertText.text` value *includes* the `/n` line break that was provided in the `GET` request. Is this going to hurt me in the future? More later... ```JSON { "requests": [ { "createShape": { "objectId": "g2c5e29abb69_0_1tdL0RVf6q30", "elementProperties": { "pageObjectId": "SLIDES_API1665207381_0", "size": { ... } , "transform": { ... } }, "shapeType": "TEXT_BOX" } }, { "updateShapeProperties": { "objectId": "g2c5e29abb69_0_1tdL0RVf6q30", "shapeProperties": { "shapeBackgroundFill": { ... }, "outline": { ... }, "shadow": { ... }, "contentAlignment": "TOP", "autofit": {} }, "fields": { ... } , } }, { "insertText": { "objectId": "g2c5e29abb69_0_1tdL0RVf6q30", "text": "This is a simple text box. There is no additional line breaks, styling, or otherwise.\n", "insertionIndex": 0 } }, { "updateTextStyle": { "objectId": "g2c5e29abb69_0_1tdL0RVf6q30", "style": { "backgroundColor": {}, "foregroundColor": { "opaqueColor": { "themeColor": "DARK2" } }, "bold": false, "italic": false, "fontFamily": "Arial", "fontSize": { "magnitude": 18, "unit": "PT" }, "baselineOffset": "NONE", "smallCaps": false, "strikethrough": false, "underline": false, "weightedFontFamily": { "fontFamily": "Arial", "weight": 400 } }, "textRange": { "startIndex": 0, "endIndex": 86, "type": "FIXED_RANGE" }, "fields": { ... }, } }, { "updateParagraphStyle": { "objectId": "g2c5e29abb69_0_1tdL0RVf6q30", "style": { ... }, "textRange": { "startIndex": 0, "endIndex": 86, "type": "FIXED_RANGE" }, "fields": { ... } } } ] } ``` Simplified `batchUpdate` request schema for this simple text slide. And here's what comes back generated: ![](https://www.bentumbleson.com/content/images/2024/03/image-8.png) Generated output. Looks great, however I see that extra line break in there. It's harmless in this case, but you can start to imagine how multiple lines of text with actual line breaks, different styles, idents, etc. might struggle to be recreated properly. Let's explore that on the next one. --- ### Experiment 3: Textboxes, Text in shapes, and that pesky line break Here's my starting point this time. ![](https://www.bentumbleson.com/content/images/2024/03/image-9.png) Input Slide I'll spare you the details this time, but it's the same process I'm following: - `presentation.get` to get the JSON object of the slide that I created manually - Convert said object into `batchUpdate` requests - Send `batchUpdate` requests (133 in this case) to the API to recreate the slide in a new presentation Results: ![](https://www.bentumbleson.com/content/images/2024/03/image-10.png) Output Slide Pretty good, right? Looks like there's a little difficulty with the indent and the glyph for the extra bullets. The indent working for the second one but not the first is peculiar. In that I mean that I successfully defined how indents are set, but it was seemingly ignored on the first line. That's probably an error on my part, so we'll park that. ... Let's move on to text in shapes. Say you've got 4 chevrons (*the API would actually call this a `HOME_PLATE` shape*) with text nicely centered (vertically) and left aligned. I also changed the font to Poppins, size 6\. Same exercise as before: ![](https://www.bentumbleson.com/content/images/2024/03/image-11.png) Input This time each of our chevrons have the text shifted up materially. What happened? ![](https://www.bentumbleson.com/content/images/2024/03/image-12.png) Output The short answer is we've got an additional line break at the end of every element. More interestingly, the font and size were seemingly reset back to Arial, 14 pt. ![](https://www.bentumbleson.com/content/images/2024/03/image-13.png) Notice the extra line break here? We can look back at the raw object JSON, generated by Google: ```JSON // ... "textElements" => array:3 [▼ 0 => array:2 [▼ "endIndex" => 20 "paragraphMarker" => array:1 [▼ "style" => array:9 [▶] ] ] 1 => array:2 [▼ "endIndex" => 19 "textRun" => array:2 [▼ "content" => "Some important text" "style" => array:11 [▶] ] ] 2 => array:3 [▼ "startIndex" => 19 "endIndex" => 20 "textRun" => array:2 [▼ "content" => "\n" "style" => array:11 [▶] ] ] ] //... ``` Snippet of object JSON from `GET` request My takeaway: This feels like a bug, but it looks like the generated JSON for this slide is artificially introducing page breaks at the end of any shape/ textbox. If this isn't removed, that line break will naturally get parsed back into the `batchUpdate` method by any translation. Fine I guess, I can fix this in my translation step. My hypothesis is that since I would never need to insert any text *after* the last element of a shape, then even if the object JSON of the slide has +1 index lengths on all instances of text, it shouldn't matter since I never need to insert there. ```PHP // if the last textElement is a blank (because a linebreak was trimmed), then skip if($textElement === end($pageElement['shape']['text']['textElements'])) { if (trim($textElement['textRun']['content']) === '') continue; } // logic to build up batchRequest here... ``` Let's remove the last '/n' record if there is one. In full disclosure, this was some trial and error. My strong belief from the beginning that was the `GET` slide JSON was the source of truth and it shouldn't be manipulated in any way. If I kept it as-is, I get rogue line breaks. If I trimmed *all* line breaks, well then not only can I no longer represent a user purposefully using a line break, but then I run into an issue where my text indexes are off: ```JSON { "error": { "code": 400, "message": "Invalid requests[38].insertText: The insertion index (76) should not be greater than the existing text length (75).", "status": "INVALID_ARGUMENT" } } ``` Example of GSlides API rejecting my `batchUpdate` request. --- ### Experiment 4: All the shapes This one is a bit personal to me. Most good slides typically use shapes and iconography as a way of telling a story on the slide. If you only ever had text, you'd miss out on a lot of ways that we make our content more digestible. Here's my input: ![](https://www.bentumbleson.com/content/images/2024/03/image-14.png) Input `"requests" => array:146 [▶]` later, I got this: ![](https://www.bentumbleson.com/content/images/2024/03/image-16.png) Output No need to look for differences - they are identical! There's another 40 of so shapes defined with Slides... I'll double check those in the meantime, but my hypothesis is that it'll be fine if all of the above work as well as they did. Let's up the ante with some custom drawn lines and shapes: ![](https://www.bentumbleson.com/content/images/2024/03/image-17.png) Input I threw in a few icons-as-shapes from [SlideCub.com](https://slidecub.com/icons/heroicons?ref=bentumbleson.com) (if you're wondering why I've spent so much time with the GSlides API). First off, if you study the documentation on shapes, you get this schema: ```JSON { "shapeType": enum (Type), "text": { object (TextContent) }, "shapeProperties": { object (ShapeProperties) }, "placeholder": { object (Placeholder) } } ``` [PageElement - Shape](https://developers.google.com/slides/api/reference/rest/v1/presentations.pages/shapes?ref=bentumbleson.com#shape) There's maybe \~80 different `shapeType`'s. This explains why in the experiment above, all of the shapes rendered correctly. There's one `shapeType` way at the bottom that says `CUSTOM`. You can probably imagine where I'm going with this, but first, let's just send the `presentation.get` generated JSON through the same conversion logic above and see what we get back from Google: ```JSON { "error": { "code": 400, "message": "Invalid value at 'requests[65].create_shape.shape_type' (type.googleapis.com/google.apps.slides.v1.Shape.Type), \"\"", "status": "INVALID_ARGUMENT", "details": [ { "@type": "type.googleapis.com/google.rpc.BadRequest", "fieldViolations": [ { "field": "requests[65].create_shape.shape_type", "description": "Invalid value at 'requests[65].create_shape.shape_type' (type.googleapis.com/google.apps.slides.v1.Shape.Type), \"\"" } ] } ] } } ``` Isn't this interesting, looks like `ShapeType` as a null isn't allowed. Let's go find the raw object JSON and see how this object is defined: ```JSON 22 => array:4 [▼ "objectId" => "g2c5e6e8ec58_0_112" "size" => array:2 [▼ "width" => array:2 [▶] "height" => array:2 [▶] ] "transform" => array:5 [▼ "scaleX" => 0.0645 "scaleY" => 0.0645 "translateX" => 5346791.2475 "translateY" => 4268461.44 "unit" => "EMU" ] "shape" => array:1 [▼ "shapeProperties" => array:5 [▼ "shapeBackgroundFill" => array:2 [▶] "outline" => array:3 [▶] "shadow" => array:8 [▶] "contentAlignment" => "MIDDLE" "autofit" => array:2 [▶] ] ] ] ``` Raw JSON from `presentation.get` for the above slide with custom shapes Notice anything... missing? `shapeType` isn't even defined. According to the documentation, this value isn't optional, yet nothing is provided. Is this another bug of the API? Odd that the API will define the shape for me, but then omit the type definition, which the resulting `batchUpdate` request requires. I'll have to hard-code any missing `shapeType`'s to `CUSTOM` and see where that gets us. The result? ![](https://www.bentumbleson.com/content/images/2024/03/image-18.png) Output The takeaway: The Slides API cannot store or recreate custom lines, curve, scribbles or shapes, such as icons. All of the custom lines simply rendered a single line matching the bounds of the original. And all of the custom icons failed to render anything. A small piece of one of the icons did manage to appear, though I attribute this mostly based on how Google originally converted the SVG to a shape (via an uploaded PowerPoint). Interestingly, the other icons still "exist" on the page, they just have no area. They have assigned border colors and fill colors and still nothing renders. As they have surface area, your cursor never can "grab" them, so you have to lasso the shapes and delete them. Perhaps a better solution is to cast any `CUSTOM` shape to a `RECTANGLE` or something that makes it obvious that there was/should be an icon here, rather than in this case where they largely are invisible. ... One other gotcha - when you create a line, such as the ones I have above that are connected to the boxes, there are `LineProperties` for `startConnection` and `endConnection`. Therefore, if you create a new line that is meant to link between two newly created shapes, then you also now need to rewrite the `LineProperties`, which means I need to keep track of the `objectID` of the new shape (in my case, the few squares), so I can update the old references, or else the API will complain that you are trying to link a line to a shape on a different slide (or a shape that does not exist if you're creating an entirely new presentation). --- ## Part 3: What's next First and foremost, if you're associated with the Google Slides team, or perhaps even the Google Docs team (based on my understand that these share a lot of underlying logic), I'd love to pick your brain on how this was designed, roadmap, and any other interesting bits. If you're in San Francisco, let me know if you want to grab a coffee. `ben @ this website .com` So what lead me down this rabbit hole? Icons. I spent the first many years of my career firmly in the corporate "Microsoft" ecosystem. PowerPoint was a daily tool in my arsenal. Good iconography can make or break a slide. One strong plus of PowerPoint (vs GSlides) is that you can natively import SVG's and convert them to shapes. Google Slides, not so much: ![](https://www.bentumbleson.com/content/images/2024/03/image-20.png) Alas, no SVG import. There is a workaround however: insert the SVG into a PowerPoint slide then upload that slide to Google Slides. This process converts the SVG (likely treated as a native shape inside PowerPoint), into a shape in Google Slides. Now this is pretty tedious stuff if you want to have a full icon pack available to you. So I built a better way: SlideCub. I can take advantage of the nature of a web app to simulate "duplicating" an icon such that I can insert onto the clipboard, the obfuscated steps to create the shape based on the SVG icon. Here's how it works: ![](https://www.bentumbleson.com/content/images/2024/03/SlideCub_IconCopy.gif) SlideCub.com Icon demo Want to try it out? - Go to [Slidecub.com/icons/heroicons](https://slidecub.com/icons/heroicons?ref=bentumbleson.com) - Open a Google Slides presentation in another tab - Select an icon from SlideCub - Change to the presentation and Ctrl + V (Cmd + V on Mac) to paste. I'm building out enhanced support for teams now, but if you put in your work email and it matches a company team (that allows direct signups), you'll be redirected to join that team. Always open to thoughts. ... Lastly, I have a vision to develop a robust slide repository where you can save and create slides on demand. Instead of starting with your corporate template of 100 slides, why not pick and choose what you need from a custom collection. This is still very much in its infancy, and the above study is a reflection of my investigation into making this work. Here's a sneak peak at how it actually works: ![](https://www.bentumbleson.com/content/images/2024/03/SlideCub_SlideGeneration.gif) Generating a new presentation with saved slides via SlideCub (sequence sped up a bit) Everything you're seeing above is exactly what I did for each of the experiments: - Save slides (via a bookmarklet) - *not shown* - Access slides in a repository - Select the slides you want - Select "generate" If you're interested in being a beta tester, please reach out: `ben @ this website` ### Do you know your Uber PNR? URL: https://www.bentumbleson.com/do-you-know-your-uber-pnr/ Last updated: 2024-08-31T19:42:17.000Z ![](https://www.bentumbleson.com/content/images/2022/10/undraw_Order_ride_re_372k-3.png) *Prologue: I'm going to mainly reference the airline industry as a case study to the question posed in this article. The ideas presented can hopefully be thought of as industry agnostic, however.* ### PNR = Passenger Name Record If you've ever booked a flight, your booking probably came with a 6-character alphanumeric record locator, better known as a PNR, or passenger name record. That random string of letters and numbers is both temporary and confidential. It's a password of sorts that lets you access your reservation online and at the airport. Let me pose a question: > If you had to rebuild the infrastructure that powers airlines today, would you introduce the concept of a PNR? ### The Challenge of Legacy Airlines, not unlike many service-based industries, have been around for quite some time. On the other hand, the technology that underpins the way we as a society function has advanced materially. We are often digital-first, accustomed to convenience, tailored interactions, and seamless handoffs between technology and the world itself. To be clear, this not to say that airlines haven't adapted to modern comforts we've afforded ourselves - they have! Frequent flyer programs, online reservations, manage my booking, digital tickets, etc. Yet they have indefinitely been bound by limitation and often constructs of previous systems and iterations before them. There's a reason many industries still run a mainframe computer in their headquarters. Deepening this challenge is often that as an industry you have shared or common standards that exist for the benefit of all entities. This means even as an individual business, you may not be at the liberty to depreciate concepts that are heavily intertwined in downstream systems or processes. ### Decoupling Consumer vs Enterprise Value This brings me back to my original question. The last time you booked an Uber, whether it was ad-hoc or a scheduled ride, you probably weren't prompted or even provided a PNR. I'm almost positive one still exists, but as a consumer, having a unique identifying string that tracks the relationship between you, your driver, and the ride your on - provides no discernable value. Internally to Uber, I imagine this figure is a primary key in many operational and analytics systems. So what's the difference? When building a ride sharing platform from the ground up, with no incumbents, no existing infrastructure, and no integrations, Uber could be judicious about the data concepts it introduced for its platform. It wasn't riddled by the baggage of technical debt, and appears to have optimized for *experience*. In this manner, technology fades away. The pipes and wires that connect riders to drivers millions of times a day feels as seamless, fluid, and natural. Airlines have this magic too. The logistics to plan and operate thousands of flights per day, including planning the network, selling seats optimally, allocating crew, staffing airport operations, maintenance, regulatory, and more — it's a elegant dance that few consumers understand. ### Building for the future I'm not the first one to suggest graduating beyond legacy constructs that may not make sense in todays world, and I understand that change is often slow and gradual. Particularly when there are no off days for operations, and maintaining the effectiveness of production systems requires careful planning and execution. I'm encouraged by [industry initiatives](https://www.iata.org/en/programs/airline-distribution/retailing/ndc/?ref=bentumbleson.com) that seek to move the airline industry forward in one way or another, and believe it reaffirms this concept of viewing technology as an enabler rather than set of tools. It allows you to reshape problems is if there were no rules. Even if the end state is not possible, charting a course towards ideal is better than simply planning for "next". To quote one of my favorite articles recently, [Extreme questions to generate new, better ideas](https://longform.asmartbear.com/posts/extreme-questions/?ref=bentumbleson.com): I hope the above thought experiment ***"jostles you out of tiny thinking"*.** ### Building a Golden Tee Fore Cabinet: Part 3 - Fix Trackball URL: https://www.bentumbleson.com/building-a-golden-tee-fore-cabinet-part-3-fix-trackball/ Last updated: 2022-10-23T18:39:54.000Z *Check out [Part 1](https://www.bentumbleson.com/building-a-golden-tee-fore-cabinet-part-1/) and [Part 2 ](https://www.bentumbleson.com/building-a-golden-tee-fore-cabinet-part-2-emulation/)to get some background on this project, including how I went about building an actual arcade cabinet, as well as specifics on how I built a custom PC to emulate the game in MAME.* --- ### Background ![](https://www.bentumbleson.com/content/images/2022/05/trackball_bad.gif) Trackball woes. If you followed Parts 1 and 2, I have effectively built a custom Golden Tee Fore arcade cabinet, I have everything installed and previously tested working, and I go to rip my driver off the first tee and the club sputters, stops randomly, then dinks the ball a fraction of the distance it should go. What's going on here and why didn't I see this in my testing? Up until this point in time, the only testing I had done with the actual trackball was physically holding the trackball assembly with one hand, and flicking the ball forward with my other hand. With that limited amount of force, the game worked fine in all my testing. It was only when I was able to actually mount the trackball, and give it a good proper swing that I ran into this issue. --- ### What's the problem? To understand what's happening here, you need to understand how these analog trackballs work. When you move the trackball around, you're turning two wheels, one for the X-axis, and one for Y-axis. These "wheels" are slotted, typically with 24 openings. Rather than detecting the speed of rotation at the axel that turns the wheel, an optical sensor counts how quickly the pulses come in from the spinning wheel. ![](https://www.bentumbleson.com/content/images/2022/05/LightSensorExample4.gif) Here's a rough depiction of what's happening here. Each rotation that crosses a slot triggers a "pulse" and the arrival of those pulses get converted into an acceleration of sorts that a computer interprets as movement. The problem is that if you spin the trackball too quickly, this wheel rotates so fast that the sensor doesn't actually detect any movement, or it only detects a fraction of the actual pulses it should, which translate into way less movement to the cursor (golf club) then intended: ![](https://www.bentumbleson.com/content/images/2022/05/LightSensorExample3.gif) A MAME Dev confirmed this behavior: ![](https://www.bentumbleson.com/content/images/2022/05/image.png) The phenomenon referenced is the [Wagon-wheel effect](https://en.wikipedia.org/wiki/Wagon-wheel%5Feffect?ref=bentumbleson.com). --- ### Proposed Solution So I went through a few different iterations for this and landed on something relatively straight forward: **I can listen for the acceleration of the trackball, and when I think it's about to bottom out, I artificially inject a large movement that the game interprets as a very strong swing.** This works because it's *proactive* rather than *reactive.* In that, if I am listening for a dropout, then it's already too late to respond since the computer would have already received a counter-intuitive reading, such as a "0" rather than a really high number signifying movement. And once the game reads a "0", then your swing is already ruined. To solve for this, we have to approximate when we think the trackball is about to bottom out, and inject movement. ### Prerequisites for this solution **Short answer:** Linux **Longer answer:** I think the difficult part of this challenge is being able to read, in a near-real time capacity, the inputs from your trackball. Not only that, but artificially injecting movement as if it were your trackball. Turns out all Linux systems have a generic input event interface that (to quote [Wikipedia](https://en.wikipedia.org/wiki/Evdev?ref=bentumbleson.com)): > generalizes raw input events from device drivers and makes them available through character devices in the `/dev/input/` directory Luckily I was able to find `[python-evdev](https://python-evdev.readthedocs.io/en/latest/?ref=bentumbleson.com)` which provides a Python-bindings to this interface, which makes it materially easier to work with. --- ### Code and Walkthrough: As a disclaimer, I won't claim this is a perfect solution, but it has been working for me for a few months now. I've played around with some other approaches that did not work as intended. Anyway, here's basically what's happening in this script: - Find your trackball device (line 32) - Every time you move the trackball, an event is generated, and I'm specifically listening for relative movements that are on the Y-axis (vertical) - Each time an event that meets that criteria is detected, I capture the relative movement value, as well as the timestamp. - I then use the previous event to compute an arbitrary "acceleration" of sorts. - If that "acceleration" is greater than a certain threshold (suggesting that you smacked the trackball but it probably isn't going to register), then I send my own input of a very large "relative value" change, 10 times at 120hz. - The loop continues indefinitely. If you run the program and keep the `print` line in, you'll get something like this when you start moving the cursor. ![](https://www.bentumbleson.com/content/images/2022/05/screen01.jpg) - The first line defines the device being used. In this case, `HID 1241:1111` is my Suzo Happ trackball - Next, each line has four values: The calculated acceleration, the current relative movement, the last relative movement, and the difference in milliseconds between the last two events. Notice how in many cases, the calculated acceleration is 0? This is because the relative movement of the trackball hasn't changed. If it was moving `-1` at one interval and `-1` at the next interval, then it's moving the same speed, it's just still moving. If you look on the far right at the really long numbers - that is the difference in timestamps. If you divide by 1, you'll get the approximate "refresh rate" of the trackball. In my case, seeing a difference of 0.008 seconds / 1 = \~125hz refresh rate. This tells me that even when you're barely moving the trackball an "event" of that movement is being sent up to a hundred times a second! Now look at some of the bigger numbers on the far left side, -15k and 15k. These numbers themselves are very arbitrary, but it's their magnitude that's meaningful. As the trackball starts to accelerate very slightly from `-1` to `-2` I'm capturing that as some form of acceleration because that change happened over 0.008 seconds. In my case, the numbers of negative (perhaps again because of how I mounted the trackball?), but a negative number is an *acceleration*, and positive number is a *deceleration* suggesting the trackball is slowing down. Here's what happens when I smack the trackball. You can roughly follow the progress of me "detecting" the large change in acceleration within a fraction of a second, and injecting movement to counteract some of the "stalling" or "jittery" behavior we know causes your swing to fail. ![](https://www.bentumbleson.com/content/images/2022/05/screen02-annotated.png) I highlighted the -125k "acceleration" value here as I set a threshold of -100k as when I believe a "large" swing was detected/ is in progress. In fact, you can see the very next line has an acceleration in the billions (again the number here is irrelevant, but the magnitude is important) suggesting that I successfully *predicted* a large movement. And although I assume my script captured it, notice that it took another fraction of a second (after another \~10 events) for my artificially injected event to be registered. In this case, each `print` statement includes two events, so seeing an event say `-35 -35` is a pretty clear sign to me that two of my artificial events are being seen back-t0-back. ### Does it feel stiff? I know what you're thinking... if I take any movement that even looks like a strong push of the trackball and convert it into a huge forward momentum, won't that make all swings in the game identical? Not at all actually. In fact, I think simply because of the impreciseness of this script running and how quickly it can actually inject the movement, the swing is often subject to the imperfect nature of how it was accelerated as the artificial movement are seen right alongside actual ones. Secondly, your swing in Golden Tee is also subject to how your X-axis acceleration is, and this script does not impact that in any way. So if you think you're going straight, but crush the trackball a few degrees off center, then that's what gets reflected in the game. The only caveat and area for improvement I'm exploring is when you do some of the extreme ball-spin moves, such as an "A1" or "C3" where you're effectively swinging the club at some 45 degree angle in both your backswing and forward swing. In my testing, the impact of these swings is slightly dampened, though I suspect this is more because of how I figured my MAME sensitivity to X and Y separately rather than anything this script is doing. The end result, a Golden Tee game that is really playable! ![](https://www.bentumbleson.com/content/images/2022/05/trackball_good.gif) --- ### Run from startup As I mentioned in [Part 2](https://www.bentumbleson.com/building-a-golden-tee-fore-cabinet-part-2-emulation/), you'll want this script running anytime you're playing Golden Tee. In my case, since that's the only thing the machine does, I have an autostart script that triggers this to run at the same time I auto-launch Golden Tee. Same as before, just a simple shell script: ```shell #!/bin/bash cd Desktop/goldentee/ qterminal -e python3 fix_trackball.py ``` Find whatever "autostart" or equivalent your operating system uses and link to your script, again, in my case this is LXQt via Lubuntu: ![](https://www.bentumbleson.com/content/images/2022/05/screen08.jpg) --- ### Some other things I considered If you follow the above, you're probably thinking there a better way to do this, though my trial and error suggested to me that this is partially a game of optimization. If you spend too much "time" calculating the movements, then you risk not injecting them fast enough for it to matter. Since we can't "block out" the actual events coming from the trackball, which will go haywire at some point due to the above described effect, our best bet is to just inject so many large movements that the game "sees" this movement and reacts with a big swing before anything else happens. One of the other avenues I considered was making the "value" I inject relative to how fast I felt you were accelerating. Something like: ```python if accel < -100000: #this rough math tries to create a linear relationship between the calculated acceleration and relative trackball movements # scale is approximately -100k = +35 relative Y; -200k = +45 relative movement # the clamp method here ensures you don't send a value > 45, which is the largest human-achievable acceleration value I was able to get myself approxRelMovement = clamp(round(accel / -4400)) pushValue(approxRelMovement) ``` The issue I received here is that I basically limited the output `value` to something between -35 and -45\. Both of which are very large swings. However I was having to do so much math to convert some arbitrarily huge acceleration back to a relative movement value that I think I "missed" the window to actually make an impact. When I tested this, despite seeing evidence of sending a string of values between -35 and -45, they all were too "late", and the game often saw some of the rogue -1 / -35 values which caused the swing to jitter further. There's another train of thought related to what classifies as "acceleration". I arrived at the `-100k` figure mainly through trial and error. Actually my first approach was to just look for a big relative movement (say -20+) and use that as the proxy for injecting movement. The problem here is that you may not ever actually get an event for a "big" swing. If you hit the trackball so fast that it accelerates so rapidly, it may jump from it's first reading of some minor acceleration (say <10), to immediately too fast to actually register. In which case, the next event detected could very well be a small number as the wheel bottoms out again and only picks up a minor movement, rather than the really-fast reality of the spinning wheel. --- ### One Last Plug ![](https://www.bentumbleson.com/content/images/2022/05/PXL_20220124_165033518--1-.jpg) I completed this project back in late 2021\. It was a multi-month effort to scope out, design, mill, assemble, paint, build, configure, and setup this custom unit in my basement. I've played a dozen or so rounds, and it's always a conversation piece with friends and family over. I'm still scoping out my next project, but this one was a ton of fun. It was a pain-staking process of trial and error, but I'm very pleased how it turned out. If you want to support me and this effort, I'd encourage you to check out my design plans that are specific to the creation of the actual showpiece unit. My blog of Part 2 and 3 related to the hardware/ software and trackball fixes is my opportunity to give back to the community with the hope that there may be further development in optimization for the franchise. Thanks for coming on this adventure! [Loading...](https://btumbleson.gumroad.com/l/gtfore-cabinet-design-full?ref=bentumbleson.com) ### Building a Golden Tee Fore Cabinet: Part 2 - Hardware and MAME URL: https://www.bentumbleson.com/building-a-golden-tee-fore-cabinet-part-2-emulation/ Last updated: 2022-10-24T03:47:32.000Z *If you haven't already started with [Part 1](https://www.bentumbleson.com/building-a-golden-tee-fore-cabinet-part-1/), I'd encourage you to check it out first. It adds a bit of context to this project and gives a rather detailed review of how I was able to assemble a Golden Tee Showpiece cabinet.* --- ## Build or Buy? ![](https://www.bentumbleson.com/content/images/2022/04/image.png) Good luck getting working original hardware! At the time when I had made the decision to build a Golden Tee cabinet, I still wasn't convinced on how I actually intended to play to game. eBay is littered with original hardware from some of the older Golden Tee games, but I also knew the game could be emulated via MAME. While my preference would always be to get original hardware, I was mostly concerned being able to get a playable version of all of the courses as well as having a sound bit of hardware. The version I had as a kid had perpetual issues with "low voltage" amongst other oddities that likely reflected more than a decade of wear and tear at a local bar. So let's scope it out roughly on eBay. In this case, I was poking around for the Golden Tee Fore! hardware, more on that shortly. - **Original arcade board -$300** (?) ... I couldn't even find a working version of the board online. All the listings I found said "not working", and they were still selling for a few hundred dollars. Given how niche the game is now, this isn't likely the case of some bar owner piecing out their own arcade; it's likely more a reflection of an experienced arcade technician trying to recoup costs on a broken board hoping that someone will buy it thinking they can fix it. - **Power Supply - $80** ... a few random arcade parts shops seem to carry old power supplies here. If you go this route, you'll have to match the power supply to the board (red or green). It's also unclear to me if you'll need additional adapters. - **Hard Drive/ Full Game "Conversion Kits"** \- $185 ... this gets a little fuzzy to me. Basically the way I understand it is if you want to upgrade any Golden Tee Fore! to the "complete" version, which gives you \~30 courses from the whole Fore! series, you need a version of that "complete" game on a new hard drive (or one of these mini extension drives), plus some compatible security chip. I admittedly didn't do much digging if you could procure this yourself, but it seemed like someone had found a niche selling these afternoon for almost $200 a pop, so it seemed like this probably was something not easily reproducible. I'm skipping the specifics, but the above alone is already \~$560 for used hardware. On top of all of this, you'd still need to figure out how to connect the game to the control board you built/ bought separately. I'm sure there's a ton of JAMMA harnesses and related guides, but this wouldn't be cheap. I relegated myself to emulating the game. ### Which game? If you check the history of the game, you'll see more a bunch of different versions throughout the years. Golden Tee Gold, Golden Tee 2K, Golden Tee Fore!, Golden Tee LIVE, etc. My goal with the project was to be able to play the newest version I could, though the modern games made this materially more tricky. In 2005, Golden Tee LIVE came out which materially solidified the online nature of the game. It also introduced more complicated hardware making it difficult to piece apart and reassemble. Not to mention these online versions required you to "phone home", utilize a subscription, and pretty much indefinitely have an internet connection. I'm not sure how this would work if you were "pirating" the game, but had original hardware that you had procured all over the internet. Nonetheless, somewhere around 2017, `Golden Tee Fore! Complete` was successfully dumped and playable in MAME. I'll talk more about this shortly, but at the time, the game was probably one of the most computational expense games to emulate requiring premium hardware. --- ## Emulation with MAME ![](https://www.bentumbleson.com/content/images/2022/04/gtfore_emulated.png) My desktop emulated GTFORE06 at \~200% - this could work! If you've ever played a Gameboy emulator or anything else that emulates an old game console you're vaguely familiar with the concept: original game hardware can be emulated at the expense of hardware limitations. For old games like a Gameboy game, almost any modern device including your smartphone can emulate the game almost perfectly - there's chipset you're trying to emulate isn't as complex as modern games, and your modern hardware dwarfs the computational abilities. This gets tricky with more modern hardware. Modern, functional emulators for consoles like the PS3/ Xbox/ GameCube are partial at best. I think the sweetspot for emulators rests in the PS2/ Dreamcast era, but I digress. Nonetheless: the takeaway is the more demanding the game, the better hardware you need on the other end to run it. Golden Tee Fore! represents one of the more hardware-intensive games that folks have tried to emulate. Luckily computers have gotten pretty good, and good hardware has gotten cheaper. I downloaded MAME .230 and a copy of the game and fired it up. Notwithstanding the terrible controls mapping to a keyboard, the game was playable. On my Ryzen 5600x, the game was running at more than double the framerate needed! This is far from scientific but I knew I was in the right place. Let's get down to business: ### CPU The name of the game here is cost vs value. I knew I was going to build a standalone PC to stay in the cabinet indefinitely. Given that all this computer will ever do is play Golden Tee, getting too powerful a CPU would be a waste of money, but not powerful enough would be a waste. MAMEUI Benchmarks was an invaluable starting place: [http://www.mameui.info/Bench.htm](http://www.mameui.info/Bench.htm?ref=bentumbleson.com) At the time, the latest version of MAME for me was .230, but even with newer versions, results looked negligible: ![](https://www.bentumbleson.com/content/images/2022/04/image-2.png) The way to read this is that each score represents a percentage the game was able to run versus baseline. So 100% means the game ran, on average, at exactly 100% of the speed it was intended to run at. So for example, you can see that "dkong" (Donkey Kong) emulates somewhere in the \~4000% percent range. You'd never play the game at 4000% speed, but it demonstrates on this hardware (an i7-6700k in this case), that the game runs with more than enough headroom. In our case, we're interested in "gtfore06" at the bottom there. Looks like somewhat regardless of MAME version, the game runs somewhere around 210% on an i7 6700K, a 2015 pretty high end processor (\~$350). This percentage is, of course, an average so I'd venture the real "playable" rate is probably some fraction of this score, but there's a good buffer in there that the game probably runs pretty well with this hardware. This is corroborated by a MAME Dev suggesting you pretty much need a 4GhZ Core i7: ![](https://www.bentumbleson.com/content/images/2022/04/image-4.png) Aug 2020 post suggesting CPU specs for "high end" games like Golden Tee My thought process at this point is if all these MAME recommends this \~2015 $300 processor, maybe I can get a modern \~2021 processor that performs the same way. I narrowed my search on CPUMark and sure enough, a Q1 2021 i3-10105 was benchmarked as almost identical to a 2015 i7-6700K. Crazy! The bonus: an i3 is a lower wattage CPU so it can be used in smaller form factors with less cooling, and it's a fraction of the cost. In my case, $90 (*regularly around $120, I got it on sale*). Don't confuse the 10105 with the 10105F - the latter of which has no integrated graphics! ![](https://www.bentumbleson.com/content/images/2022/04/image-3.png) --- ### Case/ Motherboard/ Power Supply I went back and forth with this decision for awhile. There's tons of great MicroATX cases out there I could pick up for a $100\. Throw in a cheap motherboard, and a simple power supply and I could have the a barebones PC for maybe $300? While I'm digging around for the right case + motherboard combo, I stumble upon barebones PC. I hadn't thought much about it, but little devices like an Intel NUC might not be a bad play. After many rounds of reviews, my unequivocal recommendation: **ASRock DeskMini H470** ![](https://www.bentumbleson.com/content/images/2022/04/image-5.png) This case has everything. It's small, supports LGA1200 up to 65W TDP - the exact specs of my i3-10105, it includes a power supply, has a motherboard already, and space for an NVME or SSD drive. And at $190, it's a great deal versus buying each piece yourself! --- ### Hard Drive/ RAM I wouldn't overthink this part. I picked up the cheapest NVME I could find. I figured an SSD or NVME would render the best performance and I didn't need more than probably \~25gb for the full OS plus game files. The smallest drive I could find was a 256gb - oh well, more than I need. For RAM, the difference between a 4GB stick and an 8GB stick was literally a few dollars. Might as well spend a bit more here and make sure there truly aren't any bottlenecks. --- ### All in PC Costs ![](https://www.bentumbleson.com/content/images/2022/04/PXL_20220312_183302719.jpg) Disregard the Barracuda drive - that was for another build! - $190 ASROCK DeskMini - $90 Intel Core i3-10105 (*got it onsale! Don't confuse with 10105F*) - $35 8GB RAM stick - $32 NVME drive \= \~$347+ tax ![](https://www.bentumbleson.com/content/images/2022/04/PXL_20220312_191853885.jpg) Quick assembly! And that's it. Really simple to assemble. --- ## What OS to use? Honestly this is a personal preference question. I had looked at many of the various "loaders" that exist for MAME as a way to launch the game elegantly when firing up the computer. LaunchBox, HyperSpin, GameEX, RetroFE, etc. Some are Windows, some are Linux. At the end of the day - I decided I didn't need this. I was intending to play one game only, ever, on this PC so installing a specific front-end to "launch" the game was unnecessary. I figure I can just write a script to autoboot MAME and go straight into the game, no fancy launcher needed. So the name of the game then becomes optimization. I've had poor experiences with Windows breaking down over time, and I really didn't want to deal with licensing, even if I picked some old hacked Windows XP build. It was way simpler for me to just pick a Linux build. I had worked in Ubuntu in the past and always had a good experience with it. Easy enough... except... what makes Ubuntu powerful is GNOME, it's desktop environment. Back to my original argument - overkill. What's the lightest weight OS I can run (that I'm familiar with)? ![](https://www.bentumbleson.com/content/images/2022/04/lubuntu.png) Enter [Lubuntu](https://lubuntu.me/?ref=bentumbleson.com). Basically "lightweight-Ubuntu". Download and install with [Etcher ](https://www.balena.io/etcher/?ref=bentumbleson.com)and we're ready to go. --- ## Running Golden Tee in MAME First off, I'm not going to walk through installing MAME. There's tons of guides around, so I'd encourage you to follow any you find, or just reference the [official site](https://www.mamedev.org/?ref=bentumbleson.com). In terms of getting Golden Tee (or any other game) running in MAME, that process is well documented as well, but I'll give you the high level. You need two things: - golf\_fore\_complete\_v6.00.01.chd - iteagle.zip Think of the CHD as the "game files". The file should be somewhere near 1GB. ITEagle is the BIOS used to run the game. It's a small file. Google is your friend. If you have a stock MAME install, the easiest way to do this is put the CHD in a folder called "gtfore06", and iteagle.zip in the "roms" directory. ![](https://www.bentumbleson.com/content/images/2022/04/image-7.png) Disregard this is from Windows, the process is the same. ![](https://www.bentumbleson.com/content/images/2022/04/image-8.png) Here just put the CHD And when you launch MAME, you should see the game: ![](https://www.bentumbleson.com/content/images/2022/04/gtfore_mame.png) And if you launch the game immediately, you might see a warning like this: ![](https://www.bentumbleson.com/content/images/2022/04/screen0.jpg) This can be safely ignored. Don't worry, it's completely harmless. With the game now running on the hardware I intended to run it in, I grabbed a screengrab of the emulation speed while at the menu screen. This screen simulates a number of different shots and is a realistic representation of the gameplay. In my case, the game hovered somewhere between 80% and 150% emulation speed, only dipping when I experienced heavy shrubbery or trees that likely required a little more horsepower to render. In this case, a mid-air shot was being rendered at 97% of the expected framerate. I'd say this was a pretty good cost optimization! ![](https://www.bentumbleson.com/content/images/2022/04/screen07.jpg) Game emulating at roughly 100% on the i3-10105 Anyway, there's a few things you'll need to go in order to get moving: ### Enable mouse as an input From the main screen of MAME, select `Configure Machine` then `Device Mapping`: ![](https://www.bentumbleson.com/content/images/2022/04/device_mapping.png) Then change the `Trackball Device Assignment` to `mouse`. In my case, the trackball I'm using is the original SuzoHapp trackball with built-in USB encoder. In Lunbutu (and I suspect Windows as well), this means it gets treated as a mouse. This is perfect for our use case, all we need to do is discern X/Y movements. ![](https://www.bentumbleson.com/content/images/2022/04/device_mapping2.png) Don't forgot to hit "Save" on the following screen. In your MAME root directly, you'll see a new `gtfore06.ini` which tracks all the settings related to this machine. Another day I'll explore this and see what else I can optimize for. For now, all I care is that I can use my trackball. ### Map Buttons Next up, let's remap the game controls to our control panel. If you built your control panel in the manner I described in[ Part 1](https://www.bentumbleson.com/building-a-golden-tee-fore-cabinet-part-1/), and my[ build guide](https://btumbleson.gumroad.com/l/gtfore-cabinet-design-full?ref=bentumbleson.com)... Then your buttons are likely mapped into some sort of encoder that treats your buttons almost as a mini-keyboard type input: ![](https://www.bentumbleson.com/content/images/2022/04/PXL_20220430_150936643.jpg) My mini-USB encoder for the five game buttons. To change the controls, we need to launch the game and get to MAME's options for this machine again. In my case, `Tab` on my keyboard got me to this screen by default. From here, select `Input (this Machine)`. ![](https://www.bentumbleson.com/content/images/2022/04/gtfore_TAB.png) Use "TAB" with the game running to modify MAME options ![](https://www.bentumbleson.com/content/images/2022/04/screen05.jpg) Button mapping. Using `Input (this Machine)` is valuable because the options are naturally-named to reflect the actual inputs. The game itself really only has 5 buttons: - Look Left - Look Right - Fly-by - Backspin - Start/ Options Select each option, then click the button on your control panel. In my case, they populated as `Joy 1 button x` suggesting to me that the encoder was being viewed in the game as a joystick. You can tell every option above that is "gray" means that I changed it from the default. ### Map Trackball/ Sensitivity Next, setup the trackball. From the same previous screen, you should only need to set - `Track X Analog` \= general X movement, simply move the trackball left or right. In my case, it says "Mouse X" since my trackball is being viewed as a mouse. - `Track Y Analog` \= general Y movement, simply move the trackball up or down. In my case, it says "Mouse X" since my trackball is being viewed as a mouse. But what about? - Track X Analog Inc - Track X Analog Dec - Track Y Analog Inc - Track Y Analog Dec These are inputs for *digital* controls, which Golden Tee never had, so if you use them, you're effectively mapping a digital control to an analog one which isn't as authentic. In my case, I never touched them. I'm guessing the default `RIGHT` `LEFT` `DOWN` `UP` relate to a keyboard, a digital input, but since my control panel for the game doesn't have these keys, then no one would be able to use them anyway. I suppose you could delete them out if you really wanted. I will caveat **this is highly specific** to however your trackball is registered on your machine. In my case, the trackball looked just like a mouse, so it was easy to configure. Once you have the trackball predictably working, you'll want to dial in sensitivity wise. This is very subject to opinion, but here's what I found worked best: ![](https://www.bentumbleson.com/content/images/2022/04/screen06.jpg) I'm pretty sure "Digital Speed" represents the speed that digital controls map to analog ones. For example, if I used my keyboard `DOWN` how quick will it accelerate is the Digital Speed. I apparently tweaked these in my testing, but they aren't relevant. The only thing you care about is "**Sensitivity**". There's some reading online that suggests the best figure is 25/25, however I found a more natural feel as 40/65\. Specifically because if you haven't played Golden Tee, it can be really easy to accidentally change clubs when you're trying to move into a backswing, so I opted to have the X sensitivity lower than Y. Again, play around with this to what feels right. I also had to turn on `Track Y Reverse` to `On`. I don't remember exactly why, perhaps the way I mounted the trackball? ### Lastly, get audio working If you fired up the game and didn't hear any sound or it was exceptionally quiet for your speakers, you'll probably want to go into the actual game settings to configure this. In my case `F1` launches me into the "Operator" panel for the game. In here, check `Volume Settings` and slide that into a good range. You can also change the difficulty of the game here if you're an experienced Golden Tee player, reset the player leaderboards if you want to set your own records with friends, etc. You can also run some of the `System Tests` if you want to confirm your buttons and trackball are working as expected too. Not too much here, but a good menu to know about. ![](https://www.bentumbleson.com/content/images/2022/04/gtfore_F2.png) Use "F2" to enter the settings within Golden Tee --- ### Autostart the Game If you're like me and you built a custom cabinet to only run Golden Tee, then you might be interested in launching right into the game. The easiest way I found to do this is to just create a shell script that Linux can run when the computer starts which simply instructs MAME to run the game. Here's my script: ```shell #!/bin/bash cd Desktop/goldentee/ mame gtfore06 -skip_gameinfo ``` Next, set this script to "autostart". Because I'm using Lubuntu which uses the LXDE desktop environment, I have to use the `LXQt Session Settings` \> `Autostart` to configure this script. In this case, just add a new record and point the "command" to the location of your shell script you saved above. ![](https://www.bentumbleson.com/content/images/2022/04/screen08.jpg) Now whenever the PC turns on, the game should automatically launch! --- ### Trackball Woes ![](https://www.bentumbleson.com/content/images/2022/04/trackball_bad.gif) Rough swing, eh? If you're using an original trackball and you go to smack your drive you may hit an issue like the above, where the swing jitters and ultimately kills the effectiveness of your drive. This is an unfortunately side effect of using an analogue trackball where the input has difficulty being converted to a digital input for the computer and back into MAME. Now, I was able to fix this: ![](https://www.bentumbleson.com/content/images/2022/04/trackball_good.gif) Working drive! But that's for next time! --- If you missed Part 1: [https://bentumbleson.com/building-a-golden-tee-fore-cabinet-part-1/](https://www.bentumbleson.com/building-a-golden-tee-fore-cabinet-part-1/) And if you want to build your own arcade cabinet, and show some support for this effort on my end, I sell the design plans as well as an even more detailed write-up specific to assembling the arcade cabinet: [Loading...](https://btumbleson.gumroad.com/l/gtfore-cabinet-design-full?ref=bentumbleson.com) And Part 3: [Fixing your Trackball](https://www.bentumbleson.com/building-a-golden-tee-fore-cabinet-part-3-fix-trackball/) ### Directionally correct and not precisely wrong URL: https://www.bentumbleson.com/directionally-correct-and-not-precisely-wrong-sasd/ Last updated: 2022-10-24T03:51:28.000Z ![](https://www.bentumbleson.com/content/images/2021/12/undraw_right_direction_tge8.png) I was reflecting recently on what makes a software development effort successful. Effective planning? A capable team? Supportive stakeholders? There's probably a robust answer here alone, but what caught my attention in hindsight is when small initiatives towards a larger goal don't materially contribute to success. You wouldn't know that at the time; work is undertaken on a basis of its perceived value, but it's often in hindsight, without the "fog of war" that reveals false starts. > False starts are often an effect of decisions being made prematurely. Think about the role of an executive. Removed from operational responsibilities, they think in generalities, and produce a vision for the future that often has rough edges. If you, as an executive or individual contributor, were scoping a large undertaking, you're treading the line between sitting in the "scoping" phase for too long before executing. Conversely jumping right into execution, particularly when it requires the mobilization of capital and resources, might quickly reveal shortcomings in feasibility and require a pivot, restructure, re-plan, and the related - a false start. > Avoid making specific decisions until absolutely necessary If you make specific decisions prematurely, you risk focusing on work that may not ultimately contribute to the final goal. This isn't to say you should operate in generalities forever, you just have to be strategic about decisions you make that drive work. And operating *precisely* too early may more often contribute to backtracking or lost time, rather than being *directionally correct* and making decisions as the path forward becomes more clear. As a final note, you don't know what you don't know. You can never make every decision "up front" even for the most repeatable engagements. Remaining flexible, with a focus on progress rather than velocity more often leads to successful outcomes. ### Building a Golden Tee Fore Cabinet: Part 1 - Idea and Design URL: https://www.bentumbleson.com/building-a-golden-tee-fore-cabinet-part-1/ Last updated: 2026-06-20T22:13:26.000Z If you've even been in a bar sometime in the early 2000s, odds are that you likely spotted a [Golden Tee](https://en.wikipedia.org/wiki/Golden%5FTee%5FGolf?ref=bentumbleson.com) arcade cabinet in the corner. If you're lucky, you've played it before: you recall the satisfying feeling smacking a trackball as hard as you can, admiring the crude graphics in a series of pseudo-realistic exotic courses with a dynamic gameplay, and a pair of professional commentators. It's a great bar game, and the nature of the trackball made it so unique and different from any other golf videogame you could play. ## Preamble ![](https://www.bentumbleson.com/content/images/2021/07/goldenteefore_fullCabinet.jpg) An original Golden Tee Fore! Cabinet When I was younger, my parents attended an arcade auction and picked up an old version of Golden Tee Fore for our basement. The cabinet itself was in bad shape, enduring likely more than a decade of wear in the corner of a no-name bar. The hardware was aging, its power supply often failing to provide the game board the proper voltage, while the CRT monitor often lost shades of color throughout gameplay. The version we had only included five courses, but the game provided endless hours of fun amongst family and friends. Fast forward to 2021: I spot a Golden Tee Fore 2005 cabinet online, it's a few hours away and can be had for couple hundred dollars. I inquire about the condition and start to confirm logistics: how will I get a 330lb. arcade cabinet back home and safely into my basement? I tried to procure transport; understandably the buyer was not interested in working with a shipping company, despite my generous offer. Relegated, I pondered: could you *build* a Golden Tee cabinet? There was two things I needed to validate: 1. Could I accurately re-create a Golden Tee cabinet using my limited workworking knowledge? 2. Could I procure the necessary technical components and parts necessary to play the game? Hint: yes. Let's talk about the first part here. ## Design Turns out that building and designing arcade cabinets is a bit of a niche hobby, and I was able to find a number of resources that provided direction on the matter. I knew I didn't have any of the equipment to build the a cabinet manually, but I do have some experience using CAD and CNC mills. It also turned out that these days, Golden Tee isn't sold as a full cabinet, but rather a "showpiece" that has the monitor physically detached and mounted separately. This meant to me the build difficulty was decreased somewhat materially as I could simply mount a TV and only have to build the standing cabinet. ![](https://www.bentumbleson.com/content/images/2021/07/Golden_Tee_2014_Home_Edition_-_Arizona_Sports_and_Games_7388306d-77e6-40ca-a4a2-8463295e5378.png) Golden Tee "Showpiece" Cabinet I took inspiration from a number of patrons of ArcadeControls.com, mostly notably [this thread](http://forum.arcadecontrols.com/index.php/topic,104064.0/all.html?ref=bentumbleson.com) from more than a decade ago, tracking a build from start to finish. In this case, the user, jikkjack, relied on purchasing the "Golden Tee Live - Home Edition", so he would have original hardware, including the control board, but without the cabinet itself. I'm not sure if this product combination exists any further as Incredible Technologies [sells the "home edition" for just shy of $4k](https://store.itsgames.com/products/golden-tee?taxon%5Fid=2&ref=bentumbleson.com). Oof. ![](https://www.bentumbleson.com/content/images/2021/07/GT_cab_custom3.jpg) ![](https://www.bentumbleson.com/content/images/2021/07/GT_cab_custom2.jpg) ![](https://www.bentumbleson.com/content/images/2021/07/GT_cab_custom.jpg) Jikkjack's build from 2010 documented on the [ArcadeControls ](http://forum.arcadecontrols.com/index.php/topic,104064.0/all.html?ref=bentumbleson.com)forum. Further down in the thread, Jikkjack uploaded a *Visio* file with a rough plan for the unit. Since 2011, it's been downloaded more than 1500 times, so it seems I'm not the only one eager to build my own cabinet: ![](https://www.bentumbleson.com/content/images/2021/07/GT_fore_downloads.png) Files for a GT Cabinet The downside is that the file provided was both a Visio file, which is not quite the preferred format for CNC machines, nor was it exceptionally detailed about the specifics of a cabinet. ![](https://www.bentumbleson.com/content/images/2021/07/CabMockup-jikkjack.png) The above linked Visio for an original GT Cabinet design While simple, this was exceptionally helpful - it allowed me to get the general shape and sizing for the unit. My preferred 3D design tool of choice for years has been [SketchUp](https://www.sketchup.com/?ref=bentumbleson.com). There used to be a truly free version back when they were owned by Google, but now they provide a "web" version for free, which isn't the ideal venue for real design work. Fast forward a few weeks and here's where I landed: ![](https://www.bentumbleson.com/content/images/2021/07/Screenshot-2021-07-15-224527.png) ![](https://www.bentumbleson.com/content/images/2021/07/Screenshot-2021-07-15-224549.png) ![](https://www.bentumbleson.com/content/images/2021/07/Screenshot-2021-07-15-224739.png) My Golden Tee "Fore" Showpiece cabinet design in SketchUp Some of the challenges I pondered with this design: - Unit width - Speaker placement - Access once assembled - Stability/weight - Airflow - Control panel design and access I'll review each of these, though note that there are many dependencies between them and these aren't necessarily in order. --- ### Unit Width Although the previous design linked above called for a 24.5" width, I knew I needed the unit to be wide enough to support the original control panel. My research showed that the control panel is 31 1/8" wide. So simple enough, the control panel would be 31 1/8", but then each of the "inside" support pieces would only be 30 1/8". This difference of an inch is explained by: - I have to subtract out the thickness of the sides themselves. I decided early on in the project that 3/4" MDF was going to be the wood and thickness of choice, so allowing the control panel to rest flush on top of the side panels would require I subtract out 0.75" x 2 = 1.5" - To get down to 1", I then had to re-add in 0.25" channel that was created in the sides (to allow each piece to fit perfectly). Given this 0.25" was inset into the side, the math effectively became (0.75" - 0.25") x 2 = 1" ![](https://www.bentumbleson.com/content/images/2021/07/corner_closeup_dimensions.png) ![](https://www.bentumbleson.com/content/images/2021/07/GTF_width_dimensions.png) These two views help articulate the width considerations explained above. --- ### Speaker Placement In the original tall cabinet version of the same, the speakers are mounted above the monitor and thus fire down. Not having that luxury, I was relegated to either placing the speakers on top of the unit, directly on the control panel, downward firing, or totally internal. I did some virtual test fits on the top and found that if I wanted to use a 3" speaker, I would overshoot the back of the unit. Not to mention, speakers have depth to them, so I would have to somehow extend my back panel to "fit" the speakers. Not ideal, so I shifted gears to using a downward firing approach. I figure no one plays a 15 year old game for its stellar audio, so all I needed to do was allow a speaker to be loud enough. Given the audio was downward facing and would likely reverb off the ground, I could create a small spatial audio effect. You can see in the below images that the "shelf" extends a little under 6", the last 0.75" of that comes from the width of top panel (which I've shown in a translucent pink). The extra 0.01" is a shortcoming of an imperfect design. ![](https://www.bentumbleson.com/content/images/2021/07/GT_parallel_crosssection-1.png) ![](https://www.bentumbleson.com/content/images/2021/07/speaker_placement.png) ![](https://www.bentumbleson.com/content/images/2021/07/SpeakerDimension.png) You can see the "shelf" I used to mount the speakers. --- ### Access Once Assembled I knew it would probably be smart to design the unit such that I could access every interior part if need be. Given my "shelf" based design, I did a few things: - The top shelf purposefully had gaps left on the front and back (if you were facing the unit from the front). I envisioned this mostly for wires. - That same top shelf has a large, 8" x 8" opening cut in the middle of it. I figured this was the largest size I could go without threatening the structural integrity of that panel. This would allow me to "reach" below to the second shelf if needed. - The lowest shelf would only be accessible from the back of the unit. Borrowing from the actual Golden Tee Showpiece design, I added another 8" x 8" opening, this time to add a small door allowing access to cabling and the like. - I included one final hole under the access door that I intended to put a wire passthrough. I knew I'd need to run both power and an HDMI cable out of the device, so this was an easy way to design for that without relying on a cable just randomly coming out of a hole in the unit. ![](https://www.bentumbleson.com/content/images/2021/07/backside_openings.png) ![](https://www.bentumbleson.com/content/images/2021/07/topshelf_opening.png) Views of the openings designed into the cabinet. --- ### Stability/ Weight This seems like a simple topic, but I knew I was designing a cabinet that guests may often lean into, smack a trackball, or otherwise apply pressure to it unlike most furniture. To solve for stability, I made a conscious design to design a 0.25" channel into the main sides. This mean that I could take the machined wood and effectively "slot" it right into these channels. I would of course still screw it together, but this would provide an additional level of security, removing much of potential to "torque" the unit by forcing each piece into a clean right angle. ![](https://www.bentumbleson.com/content/images/2021/07/GTF_cabinet_side_panel_closeup.png) ![](https://www.bentumbleson.com/content/images/2021/07/GTF_cabinet_side_panel_closeup2.png) ![](https://www.bentumbleson.com/content/images/2021/07/Screenshot-2021-07-15-224711.png) These views show a few closeups of the "channel" system that was designed. The middle shelf likely didn't need to be as long is it, but I felt adding more stability here wouldn't be a bad thing. Not to mention, it helped contribute to the weight of the unit. Per the weight, I'm using the baseline estimate of MDF weighing approximately 50lbs per cubic foot. If I loosely sum each piece of the unit, I end up with about 54 square feet, using 3/4" MDF sums to about 3.375 cubic feet. Doing the math, I estimate just the wood alone would weigh 169lbs. Subtract a little for the portions that are cutout. In hindsight, I probably could have gotten away with 1/2" MDF, but things just feel much more study with the 3/4". --- ### Airflow I skimped a little on this one, based on what I knew from the existing Golden Tee Showpiece, it seems that leaving openings for ventilation in the back of the unit was all that would be required. ![](https://www.bentumbleson.com/content/images/2021/07/GT_backpanel.png) Screengrab from an IT Video series showing the back of a real Golden Tee Showpiece ![](https://www.bentumbleson.com/content/images/2021/07/GT_backpanel_real.png) My version recreated. I created a series of half-inch thick, 5" wide ventilation holes. For placement, I started by splitting the back panel into two pieces (dividing down the middle), then doing that again with each half. It created this separation which felt appropriate. --- ### Control Panel Design and Access I struggled with the control panel for awhile. I knew I would need to acquire or print the actual control panel overlay, so I was stuck having to approximate the design, as well as the placement of the holes for the buttons and trackball. There's only a handful of choices out there for overlays, but I landed on the services of ArcadeOverlays, specifically for [GT Fore](https://arcadeoverlays.com/golden-tee-fore-control-panel-overlay/?ref=bentumbleson.com). Although I think $50 + $10 shipping is overpriced for what is effectively a poster print, I will give them the benefit of the doubt in terms of how they either acquired or well recreated the control panel design. ![](https://www.bentumbleson.com/content/images/2021/07/GT_printableMatReal.jpg) Golden Tee Fore Control Panel Overlay from ArcadeOverlays The primary challenges I faced was the image provided on their website did not appear to have the button holes centered: ![](https://www.bentumbleson.com/content/images/2021/07/gtf_overlay_buttons.png) The "Fly-By" button doesn't look to be centered? I ended up emailing them and was told that the "source" file they use had the buttons correctly centered, so the preview image at some point must have been incorrectly updated/ uploaded. They did offer me a PDF of the overlay for an extra $20, but given I wasn't in the business of recreating this long term, I resulted to just guessing since I needed to get my design above sent off to the shop to be milled. One other challenge was how would the trackball mount? This is notably dependent on the trackball you purchase, but wanting to stay true to original game, I tracked down the original trackball: [Suzo Happ 3" White Trackball](https://na.suzohapp.com/products/trackballs/56-0100-11HL?ref=bentumbleson.com) Thinking ahead, I purchased the [version that includes a USB/PS2 output directly](https://na.suzohapp.com/products/trackballs/56-0300-11?ref=bentumbleson.com), rather than having to rely on wiring the device up to some encoder. Also worth noting - I did not buy straight from Suzo Happ; you can find these trackballs pretty readily and for much cheaper on eBay and other sources. I think I sent just under $100. You'll also want to pick up a [mounting plate](https://na.suzohapp.com/products/trackballs/55-1101-00?ref=bentumbleson.com). The idea being that if you create a pocket on top of the control panel, you can then bolt the trackball into the mount plate from the bottom of the control panel. This prevents you from needing to have exposed screws on top of your control panel. Same eBay logic is true here as well. ![](https://www.bentumbleson.com/content/images/2021/07/mount_plate.jpg) Suzo Happ 3" Trackball mount plate ![](https://www.bentumbleson.com/content/images/2021/07/trackball-mount.png) Mounting diagram pulled from old Golden Tee users manual ![](https://www.bentumbleson.com/content/images/2021/07/GTF_cabinet_control_panel_closeup.png) ![](https://www.bentumbleson.com/content/images/2021/07/closeup_trackball_opening-1.png) Close-up views of trackball mounting hole ![](https://www.bentumbleson.com/content/images/2021/07/cutout_dimensions.png) Dimensions of the trackball cutout In the above, you can see how I based all of my dimensions off of what I was able to piece together from the documentation of the mounting plate, as well as old Golden Tee documentation. I opted to add the little "ridges" on the top left and bottom right for two reasons: 1. I knew the shape of the trackball itself would leave an opening for these so it would "force" the orientation to be correct. 2. It would add more surface area for the metal mounting plate to rest on, hopefully increasing the stability of the trackball even further. Real quick - the buttons themselves are also [Suzo Happ](https://na.suzohapp.com/products/pushbuttons/58-9111-L?ref=bentumbleson.com) brand as well. You can pick them up for a few bucks a piece from a host of online retailers. ![](https://www.bentumbleson.com/content/images/2021/07/5891xxl_mounting.gif) Suzo Happ push button details. Each mounting whole I designed as 1.125". The provided spec rounded it up to 1.13, but I didn't have a problem with 1.125" and the buttons ended up being a perfect fit. To stay true to the game you'll need 4 white buttons and 1 red for the "start" button. --- ### Tie it together One weakness of Sketchup is that it isn't quite a vector tool that often creates unexpected results. Something as simple as angles being off by fractions of a degree can have bad results. Although the "proper" tool would been a pure vector tool like CorelDRAW, I wanted to stick with something familiar. I ended up extracting each "face" of the design into a "flattened" file. ![](https://www.bentumbleson.com/content/images/2021/07/Screenshot-2021-07-15-225014.png) The final design. --- ### Design Caveats Consistent with the "weakness" of Sketchup, you can see a number of non-standard dimensions above. Some of this is inherent with the "fluid" nature of the design; I wouldn't expect some of the curves to line-up perfectly, and many of the panels have non-standard heights as an effect of needing to fit into the channels. One pro-tip I learned after awhile: the easiest way to convert a 3D shape into a 2D one is to simply scale it, reducing the Z axis to zero, such that it "removes" all elements of height. Sadly, Sketchup's native scale tool doesn't support setting an axis to zero... but I found a plugin that does! Much credit to [FredoScale](https://sketchucation.com/plugin/1169-fredoscale?ref=bentumbleson.com). In my case, I was able to compress each piece into a 2D plot. Here's an illustrative example: ![](https://www.bentumbleson.com/content/images/2021/07/default_scale.png) ![](https://www.bentumbleson.com/content/images/2021/07/FREDOSCALE.png) Left: default Sketchup scaling limitation. Right: FredoScale Box Scaling. You also need to be careful when dealing with "smooth" lines in Sketchup. I'm not sure if this limitation exists in other software, but effectively whenever you draw a smooth shape like a circle in Sketchup, it creates it from a series of straight lines. For circles, that default is 24, which means for a big enough circle, you may end up with noticeable edges. The fix is simply to bump the number of "segments" in a circle to a much higher number. I used 100 and didn't have issues. ![](https://www.bentumbleson.com/content/images/2021/07/circle_segments.png) These two circles show the difference between a 24 and 100 segment circle. Lastly, this also applies to how you draw smooth lines like some of the curves that appear on the sides of the unit. When converting my 3D design into a 2D CAD file, I noted my "curves" were no longer smooth. ![](https://www.bentumbleson.com/content/images/2021/07/image--1-.png) This view from CoreDRAW shows the "points" on my supposed smooth line. Luckily, Fredo came in handy again with their [Bezier Spline](https://sketchucation.com/pluginstore?pln=BezierSpline&ref=bentumbleson.com) plugin. Although it looks like there are tons of options to build a line up manually, I ended up using a "convert" function to map my old lines into smooth ones. Here's an example line I crudely drew to visualize, though the same concept holds true for each smooth curve on the unit. ![](https://www.bentumbleson.com/content/images/2021/07/smoothcurve.png) An example of using the bezier spline capabilities of the Fredo plugin. --- ### Send off to mill The last step was to package up the CAD file and send it to my local CNC shop. Their CNC techs mapped the cutting path into a workable shape, handling all of the tooling considerations, and laid about all pieces across two 4' x 8' sheets of 3/4" MDF. ![](https://www.bentumbleson.com/content/images/2021/07/preveiw-2.png) ![](https://www.bentumbleson.com/content/images/2021/07/preveiw-1.png) Preview of milled pieces aligned to a 4' x 8' MDF panels. If you're curious, it cost just a bit over $230 to mill this at a local shop. They ended up having to redo it because they accidentally milled the first set in 1/2" MDF vs. 3/4". The only reason I didn't keep it is because the channels and alignment of pieces, particularly on the top, were done specifically expecting 3/4" MDF. When they redid the job, it took less than an hour total, yet I was billed for 90 minutes. I wasn't in a place to complain as I'm not a regular and the team had already gone out of their way to re-mill my parts late into the night. Here's the breakdown if interested: ![](https://www.bentumbleson.com/content/images/2021/12/cost_to_mill.png) Cost to mill the above at a local shop. If I were a member, I would have saved the $10 setup fee, though I felt the price for MDF was super fair (particularly as this phase of the project was during a lumber shortage). --- ## Build it yourself If you made it this far, you're probably pretty interested in designing or building your own arcade cabinet. If you want to learn from my own trial-and-error on the project, jump start your own and get full copies of my 3D and 2D flattened designs, as well as a full build guide, plus even more pictures than I have here, consider supporting me: [Loading...](https://btumbleson.gumroad.com/l/gtfore-cabinet-design-full?ref=bentumbleson.com) --- ## Up next: - [Part 2: Hardware and MAME](https://www.bentumbleson.com/building-a-golden-tee-fore-cabinet-part-2-emulation/) - [Part 3: Fixing your trackball](https://www.bentumbleson.com/building-a-golden-tee-fore-cabinet-part-3-fix-trackball/) ### macOS Big Sur - Fix Fuzzy External Display (Force RGB Mode) URL: https://www.bentumbleson.com/macos-big-sur-fix-fuzzy-external-display-force-rgb-mode/ Last updated: 2025-02-01T22:14:01.000Z I've had this problem for *years* where I plug my Mac (dated all the way back to 10.6 Snow Leopard) into an external display, and the text appears exceptionally fuzzy for what should be a crisp display. In my case, a Philips 298P4, an old 29" ultrawide display. Everytime a new Mac version comes out, I generally have to repeat this process. Catalina and Big Sur recently made it somewhat more complicated, but I've dug through a trove of resources on the matter and believe I've found the best, and safest fix. ### The Problem Turns out what's happening is that Mac *thinks* the display is actually a TV rather than a monitor, and because of that, it changes the color space from RGB to YCbCr. You can verify this by opening up the display settings, change the resolution to "Scaled" and look for values like "1080p" and "1080i". ![](https://www.bentumbleson.com/content/images/2021/01/image.png) See the 1080p, 1080i, and 720p options? This is a giveaway that your display is being treated as a TV. --- ### The Fix All we have to do is *override* the display so that OS X uses the RGB color space. Years ago, this meant manually editing .plist files, but some clever folks have boiled it down to a simple script that will generate the correct files for us, then we just have to put them in the right place. 1) Start by downloading Andrew Daugherity's EDID Patch here: [https://gist.github.com/adaugherity/7435890](https://gist.github.com/adaugherity/7435890?ref=bentumbleson.com) 2) Unplug any other displays connected to the computer. Some recommend switching your Macbook (Pro) into clamshell mode during this step, but I've found that's not necessary as long as the only other display connected is the fuzzy display you're trying to fix. 3) Open a Terminal window and run the Ruby script from above by simply accessing the file via Terminal; an easy way to do this is just drag the .rb file from your Downloads folder directly into Terminal and hit enter: ![](https://www.bentumbleson.com/content/images/2021/01/Screen-Shot-2021-01-16-at-3.28.54-PM.png) The script will "find" your display, and patch the EDID (extended display identification data) to force RGB mode on the display. 4) The script will spit out a new directory for you, likely in your root of your User folder. The folder will follow the naming convention "**DisplayVendorID-xxxx**" where the x's can be any alphanumeric string identifying your display. --- **Sidenote** \- newer Mac's (Catalina and up I believe) will, by default, not provide you links to the root of your hard drive, so if you just open Finder, you won't see a link in "Favorites" to get to your User folder or "home". The quick fix here is select **Go > Home** from the header bar: ![](https://www.bentumbleson.com/content/images/2021/01/Screen-Shot-2021-01-16-at-3.32.45-PM.png) You can also use **Finder > Preferences** then on the **Sidebar** tab to show your home directory as a Favorite. --- 5) From your home folder, you should see the newly created **DisplayVendorID-xxxx** directory with a single .plist on the inside. Now all we have to do is tell OS X to use this file as an override. --- ### The Catalina/ Big Sur Problem (skip if you don't care) In truth, this actually started back in OS X 10.11 (El Capitan), but in short, a new "feature" called "System Integrity Protection" was introduced. This protection effectively prevents users and even apps from modifying any system files. There are workarounds that involve disabling this protection via booting into Recovery Mode, but the problem actually gets even more complicated in Catalina, and even more so in Big Sur. Starting in Catalina, the Mac OS was split into two partitions, a "system" one, and a "data" one. The "system" one, usually just called "Macintosh SSD" or "Macintosh HD" is read-only. Meaning even if we disable SIP, we still won't have permissions to mount and modify the "system" partition. Again, a series of booting into Recovery Mode, disabling SIP, and mounting the drive *could* solve this, but Big Sur brought another issue: Not only is there a separate read-only partition for the system in Big Sur, but that partition is also signed, meaning you can't just disabled SIP and mount as root. There are steps to get around this, but they are increasingly destructive to the integrity of macOS. Namely, the guides I've seen involve disabling FileVault (potentially permanently!) and potentially corrupting your Mac's ability to boot. --- ### The Easy Workaround for Big Sur and Catalina Without needing to reboot, disable SIP, or mount the core system partition, we can simply create the directory ourselves in our user (data) partition that is "linked" to the system partition, and thus recognizes the override file: 6) We need to create the Displays > Contents > Resources > Overrides directory on our data partition. The easiest way to do this is to return to **Terminal** and type: ```bash sudo mkdir -p /Library/Displays/Contents/Resources/Overrides ``` `Sudo` provides us the root access we need to modify the Library files, even though this the Library directory on our Data partition, rather than the System partition which is notably separate, protected by SIP, read-only, and signed. `mkdir -p` translates to "make a directory" and the `-p` flag, short for `parents` allows us to create multiple directories, one at each level if needed. `/Library/Displays/Contents/Resources/Overrides` is the series of folders we need to create. Odds are your machine already has the `Library/Displays` folders, but everything else may need to be created. 7) With the directories created, you may navigate to it using Finder, or simply type: ```bash open /Library/Displays/Contents/Resources/Overrides ``` And a Finder window will open to that location. 8) Next, in your original Finder window that's in your home folder, drag-and-drop the previously generated **DisplayVendorID-xxxx** folder into the Overrides folder we just opened. Mac may prompt you to authenticate or enter a password to do this action. 9) Lastly, unplug and replug your external monitor in. If it looks the same, restart your computer. All we need is macOS to start looking at our override file. Since Mac will treat the monitor as a new display based on the override, any settings including arrangement of your displays has been reset. You can be sure it's using the override if, when you open the display settings, the display name is appended with **EDID override.** It should also be materially more crisp. ![](https://www.bentumbleson.com/content/images/2021/01/Screen-Shot-2021-01-16-at-3.30.42-PM.png) You'll also notice that any other options for scaled display resolutions like 1080p, 1080i, etc. are gone. --- ### The All-In Solution (use at your own peril) Marc Bruggmann, noting the fix from above, created an all-in-one script that generates the EDID override, then creates the necessary directory and moves the files in accordingly. While I'm inclined to believe this will work, because it attempts to automate the whole process, there may be scenarios not yet encountered or coded for. Explore this approach at your leisure: [https://github.com/mbruggmann/osx-edid-overrides](https://github.com/mbruggmann/osx-edid-overrides?ref=bentumbleson.com) --- Lastly, there is a ton of conversation that has happened on the original script [Gist on Github](https://gist.github.com/adaugherity/7435890?ref=bentumbleson.com). You might check there for any further or ongoing discussion regarding this issue. ### Handling stock splits in financial applications URL: https://www.bentumbleson.com/handling-stock-splits-in-financial-applications/ Last updated: 2022-10-24T03:57:00.000Z ![](https://www.bentumbleson.com/content/images/2020/08/undraw_stock_prices_afrt.png) In Apple's (AAPL) most recent quarterly filing on July 31st, they announced their fifth ever stock split, this time as a 4-to-1 split. While companies like Amazon have historically decided against stock splits, in favor of a higher price (which may discourage short-term traders and thus reduce volatility of a stock), Apple has liked to keep their share price accessible, right around that $100 range. ### Stock splits 101 Stock splits are fairly simple concepts: on a specified day, the number of shares outstanding for a company are multiplied by the split ratio (in Apple's case, 4-to-1, or just 4), with the resulting stock price being multiplied by the inverse of that ratio (in this case 1/4 = 0.25). As an investor, if you owned 1 share of Apple on August 24th, 2020 (the planned split date), and for the sake of argument, say AAPL was trading at $400 that day, then after the split, you would have 4 shares each worth $100\. In this arrangement, no value is created or destroyed as the resulting market capitalization (outstanding shares \* share price) is still equal. ### Options? If you own options for an underlying stock, the process is a little different. Option contracts undergo an adjustment called *"being made whole"*. This adjustment follows the same principle as the stock split: no additional value is being created or destroyed. Since options cover 100 shares of an underlying stock, we have to multiply those 100 shares by the split ratio, and multiply by the inverse to adjust the strike price. Using AAPL again, you may have bought 1 Call option with a strike price of $500 (expiry date is irrelevant in this calculation as long as the contract is not already expired). After the split, your 1 Call option now controls 400 shares (recall the 4-to-1 split), each with a strike price of $125 ($500 \* 1/4). ### Reverse splits There are occasions where a reverse stock split may happen where the split ratio is less than 1, such as a 1:5 split. In this arrangement, if you owned 5 shares of a stock trading at $1, after the split you'd own 1 share @ $5\. The same math for options is true for reverse splits. ### Edge case: fractional shares Lastly, there are cases for both regular and reverse stock splits that may leave an investor with fractional shares, such as a 3:2 split with too few shares to evenly resolve to a single number. Or a reverse split, say 1:5, wherein an investor owns less than 5 shares. In both cases, any "fractional" shares are automatically liquidated into cash. --- ### Adjusted Data A company's share price drives a lot of financial calculations. We already touched on options, but there are other values such as volume, dividends, and historical prices that should be adjusted. Some might argue that historical values should be left alone, though I'm of the belief that calculations, technical indicators, or other signals may be mislead by dramatic changes in the share price. Could you imagine if you woke up tomorrow, and AAPL was worth 1/4 of what it was today? If a stock chart showed an overnight drop of 75%, I would be concerned. You also wouldn't be able to do any sort of technical analysis since you couldn't realistically compare historical days to present and future days due to the disparity. To correct this, I'll adjust all historical data accordingly. The below is written from the perspective of the database structure of Carbon: | Sprint | This is the concept at Carbon which effectively represents the asset (stock) and underlying options contracts utilized to hedge the equity. | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Stock Quote | As above, we'll divide the stock quote by the split ratio. | | Shares | Same here, we'll multiply by the split ratio | | Call Strike | We'll divide the strike by the split ratio | | Call Price | Although this cost is in the past, like the dividends, I'll adjust it so it's still accurate to make sure the total (see below) remains the same | | Call Total | This calculation is a little unique to Carbon. Yes, options typically cover 100 shares of a stock, but in the case of Carbon, we only buy/sell a "fraction" of the contract equal to the underlying shares purchased. I won't get into why this is here, and I know there's really no precedent for this on the markets today, but the idea here being that we want "Call Total" to be the same as it was previously, since this is the effective "price" we paid. | | Put Strike | Same as above, but for the put contract | | Put Price | Same as above, but for the put contract | | Put Total | Same as above, but for the put contract | | Equity Total | This is just stock quote \* shares. Since we're updating both of these, we could recalculate this, but it should always equal the same as before since no value is being created or destroyed. | **Option Strike Prices**: this is a table that lists all of the strikes for each stock during some period. It's refreshed pretty regularly so there isn't much historical data here, but rather than relying on scheduled refresh of the data, I can update the actual strike values relative to the split ratio. For AAPL, this means I'd take each strike price and divide by 4 (recall the 4-to-1 split). **Stock Price Historicals**: this table houses the open/ high/ low/ close and volume figures necessary to produce historical stock charts. For the stock prices (open/ high/ low/ close) we'll divide by the split ratio (4 for AAPL). For volume, we'll do the opposite - multiplying by the split ratio. So if a day had 100k volume, we'd adjust that to 400k volume to maintain the liquidity (since everyone now owns 4x the amount of shares the did previously, it stands to reason that the volume traded on the market in a normal day would be 4x what it was prior to the split - not because of increased activity, but because there are now 4x as many shares available in the market). **Stock Prices (Realtime)**: this is a bit of a temporary table that stores live prices as they are fetched throughout the day, at the end of the week, this data is purged. I've found this is far more efficient than a single table of stock prices since you would need to query the massive historicals table for a single price quite often, just to load your dashboard, which has the latest price for all stocks in your portfolio and your watchlist. Although this data is short lived, it should be easy enough to update the "value" and "change amount" for the stock in question. The "change percentage" doesn't need to be recalculated since this percent would not change. **Dividends:** Although I've seen this one both ways, I feel that historical dividend figures should be adjusted. Carbon calculates dividend yield based upon the last paid dividend (if it's within the past year), the expected frequency (almost always quarterly), then dividing that by the last price of a stock. For AAPL, they will pay a $0.82 dividend on August 13th (before the stock split). $0.82 \* 4 (expected frequency) = $3.28 annually / $444.45 (last AAPL price as of this writing) = Dividend Yield of \~0.74%. Consider if you left the dividend amount of $0.82 alone, but then utilized the post-split AAPL share price in a few weeks. That would move the dividend yield calculation to \~2.95% annually, which is misleading. I'll update each of the historical dividend amounts by the split amount. **Stock Metadata**: I maintain a list of the shares outstanding on a stock to support many calculations in the application. As discussed previously, we'll multiply the shares outstanding by the split ratio. --- ## Programmatically splitting Ideally speaking, I could use an API with a predictable output that gets queried on a regular basis that provides me a list of upcoming stock splits. I could ingest that data, and convert it into a job to be processed the morning of the effective date. Unfortunately I couldn't find an endpoint with the few API's Carbon utilizes that provides this info in a predictable fashion. The closest I can get is a news API, but I'd need to have a lot of rules to make sure I was extracting the correct information. In lieu of over-engineering this rare event, I created a command that generates the job necessary for the split and delays it until the morning of the effective date provided. Here's a glimpse at that command: ![](https://www.bentumbleson.com/content/images/2020/08/stocksplit.gif) When that job processes, all of the above data will get updated according to the split ratio entered. The job took a little less than a minute to process on my local machine, so I'd expect it to take no more than a few seconds running in production. We'll find out come August 31st! If splits like this become more common, I may have to find a way to include a "warning" message, or otherwise provide [some sort of feedback](https://www.bentumbleson.com/stop-designing-for-edge-cases/) to the user in a period both before and immediately after a split. ### Adventures in database query optimizations (hint: avoid N+1 problem) URL: https://www.bentumbleson.com/adventures-in-query-optimizations/ Last updated: 2022-10-24T03:59:40.000Z ![](https://www.bentumbleson.com/content/images/2020/07/database_optimization.jpg) Source: [Serj Marco](https://dribbble.com/MarcoVector?ref=bentumbleson.com) Back in May, I started to notice that some of the requests on a project of mine had gradually gotten slower and slower in the past few months. While the app itself was relatively snappy, it got to a point where in production, simple page loads were taking 4-8 seconds to display just a few tiles of data. As this was a gradual problem, I suspected the issue dealt with the queries into large tables (2M+ records). I copied my prod database back into my local environment to debug: ![](https://www.bentumbleson.com/content/images/2020/05/image.png) Here's a Laravel Telescope request log showing this particular action taking over 28 seconds in local! 28 seconds! Whew. We've got a ways to look. Let's dissect the queries involved in this endpoint: ![](https://www.bentumbleson.com/content/images/2020/05/image-1.png) I broke it up in to a few sections here. First is setup/ auth, which is handled through Laravel Passport. The next line retrieves each of the `watchlist` records. Think of `watchlists` as the custom pivot table in a many-to-many relationship between `users` and `stocks`. The next section, with a number of long running queries is an example of poorly using a `forEach` loop to fetch the stock/ stock prices associated with a `watchlist` record. You can see here I'm fetching the stock itself, then a number of attributes from the `stock_price` table. This is far from ideal. ### Eager Loading Let's fix the N+1 problem associated with the repeated nature of each stock price query above. In [Laravel](https://laravel.com/docs/7.x/eloquent-relationships?ref=bentumbleson.com#eager-loading), we can eager load relationships using `->with()`. ```php public function index(Request $request) { $user = $request->user(); $watchlists = Watchlist::where([ [ 'user_id', '=', $user->id], // match based on user [ 'isActive', '=', 1 ] // make sure sprint is active ])->get(); } ``` Original code. Not great, just loading watchlist records here. ```php public function index(Request $request) { $user = $request->user(); $watchlists = $user->watchlist_stocks; return new WatchlistResource($watchlists); } ``` This time, lets delegate the `watchlist` down to the `user` model, then we'll build up what we need for the API from `WatchlistResource` `User` model: ```php class User... public function watchlist_stocks() { $watchlist = $this->belongsToMany(Stock::class,'watchlists') // watchlist is a custom pivot name ->withPivot('date_added','date_removed','isActive'); // get extra columns from the watchlist table return $watchlist->wherePivot('isActive', 1) // only return stocks actively on watchlist ->with('latest_price'); } ``` Great, so we've made progress here. We're no longer querying for stock prices (`latest_price` is a `hasOne` relationship for the most recent `stock_price`), and we're passing all the needed data to the resource at once. Here's the actual query that gets generated to fetch those stock prices: ![](https://www.bentumbleson.com/content/images/2020/05/image-2.png) `->with('latest_price')` query takes about half a second... Hmm. You'll see we've solved the first problem with the `in` clause allowing us to return prices for multiple stocks. The flaw here? We haven't limited any results, so the database engine is having to traverse quite a few rows. Running `explain` on the query shows it's still having to access 50k+ rows at any given time, even with the foreign key index already in place. ### Introduce a LIMIT So it turns out the Eloquent, the ORM for Laravel, is already pretty capable with the way it generates queries. For example, whenever you access a `hasOne` relationship, the query will always end with `limit 1`. Probably basic, but I thought that was nifty. As you might imagine, when you're querying a large table, this has tremendous benefits as it materially reduces the number of rows that need to be touched as a part of the query. Below I'll walk through the two tries it took me to get the right syntax to correctly `limit` the query above. First, if I'm returning `n` number of stocks, then I only need `n` number of stock prices, as the method will return the latest stock price. Laravel has a handy method on eloquent called `count()`, that simply counts the number of records in a collection/ eloquent query. So if my `$watchlist` returns 10, then I can *limit* my query for stock prices to 10\. Easy as: ```php $limit = $watchlist->count(); // count the number of stocks that got returned ``` --- Quick caveat to the above: since we're limiting the number of records returned, it is very important that `stock_prices` in this instance are always in sequence of one another. So if I have 500 stocks in a database, then `stock_price.id` 0 - 499 should map to all 500 stocks. Here's an example of a **bad** scenario: | id | stock\_id | value | change | created\_at | | ---- | --------- | ----- | ------ | -------------- | | 5000 | 1 | 50 | 0.5 | 5/5/2020 17:00 | | 5001 | 1 | 51 | 1 | 5/5/2020 17:01 | | 6001 | 2 | 25 | 2.2 | 5/5/2020 17:00 | | 6002 | 3 | 35 | 3.2 | 5/5/2020 17:00 | | 6003 | 4 | 10 | 1.3 | 5/5/2020 17:00 | If the query above was looking to pull prices for stocks 1,2,3,4, then the SQL would end with `limit 4`. When this table is queried, it will start with `id` `5000`, then `5001`, `6001`, and `6002` before returning. This is a problem because we've returned two prices for `stock_id` `1` and none for `stock_id` `4`. Ideally speaking, we would only populate the `stock_prices` table in a predictable fashion such that this doesn't happen. With that being said, there are scenarios where it could happen, so we'll have to build in a little error handling in our final version. --- ### Limit Try 1 Here's my first attempt: ```php class User... public function watchlist_stocks() { $watchlist = $this->belongsToMany(Stock::class,'watchlists') ->withPivot('date_added','date_removed','isActive'); $limit = $watchlist->count(); // count the number of stocks that got returned return $watchlist->wherePivot('isActive', 1) ->with('latest_price') ->limit($limit); } ``` The flaw here, discovered through trial and error, is that the `->limit` that I've added, is being appended to the `watchlist` query, **not** the query that fetches `latest_price`. Here's a visual from Laravel Telescope: ![](https://www.bentumbleson.com/content/images/2020/05/image-3.png) See how the `limit` got added here? This doesn't really add much value. As mentioned, this `limit 10` doesn't help me. Since a `user` likely has between 0-20 stocks on their watchlist, this means the entire watchlist table is a few thousand records, max. Of course, this is proportional to the number of users, but that's not a problem I have. Let's see if we can move the query into the eager load. ### Limit Try 2: Learning from my mistakes, I need to use a callback in the `->with` rather than directly calling `->limit` on the `$watchlist` record. ```php class User... public function watchlist_stocks() { $watchlist = $this->belongsToMany(Stock::class,'watchlists') ->withPivot('date_added','date_removed','isActive'); $limit = $watchlist->count(); return $watchlist->wherePivot('isActive', 1) ->with(['latest_price' => function ($query) use ($limit) { $query->limit($limit); // limit stock price results to number of stocks we have total }]); } ``` This in turn produces a query like this one: ![](https://www.bentumbleson.com/content/images/2020/05/image-4.png) Notice how adding that limit moved our query from about half a second to less than a tenth of a second? That's a material improvement, and pushes us back to sub-second page load times. ### Error Handling I explained above how this approach assumes that you would never have multiple `stock_prices` that belong to a single `stock`, in a row. If that were the case, the `limit` would cause it to return multiple records for one stock, and subsequently miss stock prices for the "extra" stocks. The query itself will look normal, but in this case, Laravel will fail when it attempts to access a price that wasn't actually retrieved. Not wanting to sacrifice my fast query in a majority of scenarios, I added a "check" that effectively verifies all data was returned as expected. If not, it reruns the query but with an arbitrarily high `limit`, rather than the exact figure. **I will not claim this is the best method - but it works for me, and only happens very rarely.** Firstly, I added an argument to my `watchlist_stocks()` method on the `User` model for an `$unlimitedQuery` as a boolean, defaulting to false. The thought process being that when this method is accessed normally, the method will behave exactly as it does previously, relying on `count()` to get the limit figure. However, if the method is called as `watchlist_stocks(TRUE)`, then the `$limit` is arbitrarily set to 10000000\. This is far from the theoretical max of SQL limits, but I figure it's more than enough headroom to skip over any instances of out-of-sequence stock prices. Resulting method looks like this: ```php class User... public function watchlist_stocks($unlimitedQuery = false) { $watchlist = $this->belongsToMany(Stock::class,'watchlists') ->withPivot('date_added','date_removed','isActive'); if($unlimitedQuery) { $limit = 1000000; // set limit arbitrarily high so all prices are returned } else { $limit = $watchlist->count(); // count the number of stocks that got returned } return $watchlist->wherePivot('isActive', 1) // only return stocks actively on watchlist ->with(['latest_price' => function ($query) use ($limit) { $query->limit($limit); }]); } ``` Back on my controller, I now need to "check" after the query is ran for the first time if all data is present. I can do this by utilizing the `every` method, that will loop through each record returned and attempt to find the `latest_price` attribute. I then flip the equality so that the variable `$dataMissingFromQuery` will be `TRUE` if any `stock` is missing `latest_price`. In that scenario, I simply rerun the query with the `$unlimitedQuery` as `TRUE` and pass the new collection to the resource. This does add \~.5 seconds of time to a page load, but it's rare, gets logged for me, and really is way better than 30 second page loads: ```php class WatchlistsController... public function index(Request $request) { $user = $request->user(); $watchlists = $user->watchlist_stocks()->get(); // this test will return TRUE if latest_price does not exist on the stock $dataMissingFromQuery = !$watchlists->every(function ($stock) { return $stock->latest_price; }); if($dataMissingFromQuery) { // log the bad query Log::alert("Query missing data due to LIMIT: watchlistsController@index" . PHP_EOL . "Stocks involved: " . $watchlists->pluck('symbol') ); // pass "TRUE" to argument in order to force LIMIT to be very high $watchlists = $user->watchlist_stocks(TRUE)->get(); } return new WatchlistResource($watchlists); } ``` --- So what's the lesson to be learned here? If I had to boil it down: - Be conscious of the N+1 problem - Be careful utilizing database queries in a `forEach` loop - Hypothesize the realistic size of tables in production while testing - Use appropriate logging to help spot deficiencies in your code Last thing - if you're curious what app I'm referencing in this post - it's [Carbon](https://trycarbon.co/?ref=bentumbleson.com). Carbon is a (currently) paper trading platform that simulates realistic risk-conscious investment strategies utilizing market derivatives to control investment risk - all behind the scenes and exceptionally seamless. The app itself is a Vue SPA with a Laravel backend, so I'm always working to make the experience better. This post is an example as such. Thanks for reading. ### Stop designing for edge cases URL: https://www.bentumbleson.com/stop-designing-for-edge-cases/ Last updated: 2022-10-24T04:00:44.000Z I'm confident that I'm not the only person who has sunk a lot of time into designing these that may never be seen. More specifically, think about the natural flow of applications, the "happy path" if you will. Design those the first time around, but any *unexpected* error page or action probably doesn't deserve your attention for a V1 release, or especially a beta. Here's a good example: in Carbon, a financial application, stocks can be "inactive". The most common cases of this is when a company has been acquired or merged with another. Mind you this is rare at the Fortune 500 level, but it does happen. In these cases, I'd prefer not to just delete all the data I have on that company, it just needs to be put into an "archive" state. There already exists logic to "deactivate" the stock so that it won't attempt to pull new prices, etc., but to an end user, the only indication you might have is looking at the recent news citing a merger. ![Inactive Stock Warning (light)](https://www.bentumbleson.com/content/images/2020/04/image.png) Would you consider this warning message critical to users? So I built this, a simple warning header that appears when a stock is returned from my database with *isActive = 0\.* I regret this took me a few hours to get the colors, font, and icon just right. It's not perfect, but I hit a point where I went *no one is ever going to see this.* Don't fall into this trap. --- **Caveat:** this is not to say you shouldn't *eventually* design for edge cases. Odds are though, at the time you're building an application, with zero users, it's an inefficient use of time for minimal value, since the odds that anyone will see what you've designed is very low. It's also not the end of the world if your user flat out hits an generic error page. Turns out when people derive value out of your app, it's not the end of the world when one small piece wasn't as user friendly as it should be by the time you're a VC-funded company. **Last note:** attention to detail, even the small things, shows thoughtfulness in design. Though the point above remains true, if you're interested in a few examples - [LittleBigDetails ](https://littlebigdetails.com/?ref=bentumbleson.com)has you covered. --- **Edit**: I ended up inverting the colors here. Thought it would carry more "punch" when exactly 0 people run into this warning. ![Inactive Stock Warning](https://www.bentumbleson.com/content/images/2020/04/image-1.png) More punch, right? ### Invisible Engineering | Case Study: Google Stock Price Results URL: https://www.bentumbleson.com/invisible-engineering/ Last updated: 2022-10-24T04:03:39.000Z ![](https://www.bentumbleson.com/content/images/2019/08/data-back-up.png) **Source:** Matt Anderson ([dribbble](https://dribbble.com/mattandersondesign?ref=bentumbleson.com)) It can be easy to become blind to the amount of engineering that goes into websites and applications that seem so simple in premise. Google Search, uploading a photo to Facebook, sending a Snapchat, liking a tweet. All actions that happen thousands of times a second, and yet none of these services skip a beat. Yet behind this simple action is a herculean effort of engineers, designers, developers, and product teams that make it work seamlessly. It's this concept of *invisible engineering* that is so fascinating to unbox. --- ## Case Study: Google stock price results ![](https://www.bentumbleson.com/content/images/2019/08/MSFT_stock.PNG) This feature with Google has been around for years, but it wasn't until I built my own stock chart that I realized the number of challenges engineers had to solve to make such a straight forward feature work. I wanted to review a number of those briefly: ### Frequency of data (interval) Do you notice how you Google gives you a handful of time range options (e.g. 1 day, 5 days, 1 month, etc.)? Simple concept, but it's not as easy as simply returning more data from the database. Consider this: when you're looking at the "1D" option, Google is showing you stock price data in 5 minute intervals. Given the markets are open 6.5 hours a day, that's approximately 85 data points. If you kept that same 5 minute interval with 5 days, that's 425 data points. On a phone screen that's no more than maybe 3" wide, that's a lot of dots. Go one step further - consider 1 month with 5 minute intervals = 1700 data points. Google has obviously thought through this and has assigned different intervals of data that should be returned based on the overall range selected. For example, a 1 day range gives you 5 minute ticks, 5 days gives you 30 minute ticks, and 1 month gives you daily ticks. When you select a new period, you can see the async request that tells the API what interval of data it wants: ![](https://www.bentumbleson.com/content/images/2019/08/interval_check_notes.PNG) Does 86400 sound like a familiar number? Turns out that's the exact amount of seconds in a day. So when the **period\= 1M,** then return all of the associated data that is 86400 seconds apart from the last (1 day). This also gives us clues into how financial data is stored in Google databases. ### Data Structures (database considerations) I won't pretend I know what Google's infinite database of financial data looks like, but I'll make this realistic using my own mini-financial database, which might as well be a spreadsheet. ![](https://www.bentumbleson.com/content/images/2019/08/image.png) Snippet of MSFT stock price from May from 11AM to 3PM CT, 2019. When you're storing financial data, you have to consider what kind of information you need. In the case of Google's chart above, it's just price and time (X and Y), so there isn't the need for volume data, though you can see why it might be valuable to have this information in the future. Volume itself is an "instantaneous" value like price is. If someone asked you what the volume of MSFT was at 2:30PM ET on Friday, August 2nd, it might not be immediately clear if that user wants the volume since the last interval (*which would make sense if you were showing bars with underlying volume information*), or if they want the sum of that volume since the open. While both are ranges, one is dependent on the interval of the data, and one is the sum of all of the volume from the start of that day. While somewhat mundane, as a database architect, you'd need to make this decision. Same is true from open/high/low/close. If our interval is broad, such as 15 minutes, you now have data that actually represents a range, rather than a singular point in time. In Google's case, they're storing data likely down to the second, meaning when they retrieve it, they aren't returning columns associated with high/low/ close, it's just that time itself, and the ability to generate high, low, and close over some interval (say 5 minutes), will need to be calculated live. How about dates and times? Finance is a global business, so not everyone is on Eastern Time. How about daylight savings for part of the year? How do you ensure the data you're storing is associated with the time it actually happened at? One safe bet is to store the time in a time standard, such as UTC. In the above, you can see 20:00:00, which represents 4PM ET, or the close of the market. With the source data standardized, you are now free to manipulate it as you show it back to your user. If you want to get really complicated, you might need to detect the user's current location to deduce the correct timezone. This is easier said than done. Finally, what about the actual prices themselves? Since we know that historical prices can be adjusted in the future from stock splits or other actions, it would be in our best interest to store values in the smallest denomination possible: in our case, cents. If a company has a stock split ([like Apple did in 2014](https://venturebeat.com/2014/06/09/what-you-need-to-know-about-apples-7-1-stock-split-today/?ref=bentumbleson.com)), you'd either need to recursively update all of those historical records, or upon retrieval, process all prices *prior* to the split date through that split logic. ### Chart Specifics Javascript charting libraries are amazing with what they can do, though there's a host of oddities that need to be dialed in for each chart that gets generated. It's likely these micro-limitations over time that drove Google to [develop their own charting library](https://developers.google.com/chart/?ref=bentumbleson.com). How do you make sure the Y-axis is always in a correct range? This sounds easy - maybe +5% of the max, and -5% of the min and round to the nearest whole number? Easy enough I suppose, but consider edge cases including rounding and digits necessary. For example, Ford (F) floats around that $9 mark, so changes in the price are literally measured in pennies. If you hard-coded that rounding logic I discussed above, both your min and max might still be $9, or perhaps they'd even get rounded up to $10 and none of your chart would show. Can't have that. What about stocks with really high prices? An extreme example may be Berkshire Hathaway's A-class shares (BRK.A), which closed at $306K today. If you hardcoded rounding logic to nearest ten or even hundred, your scale would have hundreds of intervals on the Y-axis between $300K and $308K. There would be some much overlap, you likely couldn't even read them. Even if the scale correctly dropped extra values, the chart might end on an un-exact number, such as $306,130\. Hypothetical, but if you were only rounded to the nearest $10, this might become the "max". How about dates on the X-axis? Like I mentioned in the *frequency of data* section, your X-axis needs to be dynamic based on the data you're returning. You can't use the same scale for each interval. ![](https://www.bentumbleson.com/content/images/2019/08/image-1.png) Here's an example of what not to do. Since we can reasonable assess the width of a users device, we can start to make assumptions about how many dividers make sense. For example, with 1D and 5D, you likely want hours:minutes, whereas 1M or 3M likely make more sense as just days. Finally, large time periods, such as 1Y or 5Y, might only need the month, quarter, or perhaps just the year itself. ### Getting the Data To conclude, think about how much data is being processed by Google to be able to render this specific chart for you and millions of others on-demand, for almost any stock in the world. If you sit on the page with the chart open, you'll see Google live update the price every few seconds. If we said Google was listening for changes every 5 seconds, that's just under 5000 data points per stock, per day. Given there's something like 7000 stocks traded on US exchanges, that's in the ballpark of **35 million** stock prices per day. Given there's \~253 trading days a year, that's just under **9 billion** stock price records per year. Throw in more granular data (such as every second), pre and post-market hours, and more stocks, and we're talking about a lot of data to acquire. Since Google isn't an exchange, it has to *get* that data from somewhere, likely the exchanges themselves. So not only is Googling polling for stock prices about 80K times a minute (7000 stocks x 12 checks/ minute), they are having to process, persist, distribute, and retrieve that data almost instantaneously just so you or I can casually watch a stock price flick up or down every few seconds. --- **My takeaway:** be more conscious about the thought that went into everything you use, particularly software. Ask "how", then understand the "why" - it's an enlightening experience. ### Make Asana More Valuable: Product Study URL: https://www.bentumbleson.com/asana-product-study/ Last updated: 2022-10-24T04:06:30.000Z ![](https://www.bentumbleson.com/content/images/2019/03/asana_logo.png) In the relatively new market of collaborative work management tools, [Asana](https://asana.com/?ref=bentumbleson.com) stands alone as a [beautifully crafted](https://medium.com/building-asana/circling-ourselves-the-story-behind-asana-s-rebrand-e8247516705a?ref=bentumbleson.com), enterprise-ready, scalable, and intuitive work management solution that supports clients across the globe. In its most basic sense, Asana adds structure to teams trying to accomplishing work, allowing organizations to mature beyond simple task lists, Excel spreadsheets, or email threads. As someone who has worked extensively in the project portfolio management space (PPM), the introduction of "collaborative work management" tools have started to expand the scope, capabilities, and depth of tools in the marketplace that seek to meet changing demands from CIO's and beyond. So much so, Forrester tracks tools across multiple categories: *strategic portfolio management tools* and *collaborative work management tools* (for the enterprise). While I've traditionally worked with tools in the *strategic portfolio management tool* space, such as CA PPM, much of my recent experience has been with tools such as Clarizen or ServiceNow (which sit in the *collaborative work management* space). ServiceNow is a particularly interesting use case because its product is so broad in capability, it lands in both categories above - and does them both well! Nonetheless, this simple example is another data point in a shifting market, where tools are becoming more agile and reacting to what I'd call the "*productization of IT*". This is more so specific to the PPM tool space, but it stands to reason that while strategic tools shift to enhance work execution capabilities, then the same must be true in the other direction. So where does Asana fit? --- ### PPM Primer Before understanding where Asana sits, it helps to understand some of the tenants, or capabilities, of PPM, and what they seek to enable in a mature organization. ![](https://www.bentumbleson.com/content/images/2019/03/Screen-Shot-2019-03-10-at-9.17.38-PM.png) - **Portfolio Management:** managing the pipeline of work, planned and in-flight, with visibility to assess portfolio adherence to strategic objectives. - **Demand Management:** the process by which a request for work is initiated, usually including a business case, scoping, prioritization and fulfillment. - **Project/ Program Management:** managing individual streams of work; monitoring relative progress, understanding risk and issues, resourcing, and timelines. - **Resource Management:** the process by which an organization's labor is effectively utilized and deployed to meet dynamic project needs and timelines. - **Results Management:** a retrospective process, enabling organizations to learn, grow, and adapt from completed (or failed) work. (*\*this capability is difficult to mature and not usually a priority*) With this understanding, we can start to add structure to the types of activities each capability enables, and where it sits on a spectrum of "strategic" to "execution": ![](https://www.bentumbleson.com/content/images/2019/03/Screen-Shot-2019-03-10-at-9.19.02-PM.png) This loose diagram outlines a handful of activities, mapped to their PPM capability, and approximately correlated to a "scale" of enablement. For example, at the far left, the idea of an organization completing some sort of long range planning activity is very strategic, and usually resides in within a "portfolio management" capability. This is not unlike how Asana builds their own strategy (more below on this). Similarly, on the far right, task management is as close to actual work execution as you can get, and is firmly representative of the "project management" capability of PPM. --- ### Asana in the Marketplace As a "collaborative work management" tool, Asana sits very firmly on an execution front, and offers a host of features that support team members ability to track and execute work. On the scale above, I would place Asana about as far right as you can go. And this isn't a slight - what Asana does, it does exceptionally well, and is arguably a world leader in. While I've never used Asana in the wild, I know my company does (plus another 50,000), and they clearly derive a lot of benefit from its ability to track tasks, milestones, visually show progress with calendars and timelines, and keep everyone in sync, limiting a need for email conversations. As a leader in "project management", Asana has and will continue to introduce features that push it closer to the "strategy" scale above. In doing so, Asana is broadening the market of its tool, increasing the value of the application more broadly across an entire organization. So the remaining question for Asana management and product teams: **how do we enhance our product to better meet the "strategic" needs of our clients?** --- ### Making Asana more Strategic I've played around in my environment of Asana for only about a week now. I know it's not nearly enough time to fully judge a tool, but I felt that I was able to explore the tool as would a small startup looking for basic task management. In that world - it's a nifty little tool, fast, smooth, and pretty (*and most enterprise tools aren't, so this is a big win*). It has struck a near perfect balance of usability and function that few products are able to do. > With that being said, **there's one big limitation:** there are few, if any, strategic components of the tool that make it valuable across the enterprise. For most organizations, perhaps that's not why they subscribe to using a tool like Asana. However for the remainder that skip over it because it doesn't cohesively support the scale of work management (seen above), Asana may only be seen as supplemental tool, and not one that can comprehensive redefine how an organization works. **This weakness is an opportunity:** Asana has an ability to extend its brand, reach, and impact by growing the capabilities of their product to support more strategy and planning activities, rather than just execution. I believe a good place to start is rounding out Asana's project management capabilities. --- ### Discover: Comprehensive Project Management In enterprise, project management is a big deal. [IT projects fail all the time](https://www.cio.com/article/3068502/more-than-half-of-it-projects-still-failing.html?ref=bentumbleson.com), for starters. Modern businesses demand visibility into their portfolios and in-flight projects, data to make informed and strategic decisions about project priority, and the ability to deploy resources efficiently and accurately. While there are a host of capabilities that make this possible, an excellent starting point is status reporting. Effective status reporting enables timely visibility to stakeholders, implicitly provides approvals and oversight on progress, manages risks and issues as they arise, and keeps a project on track. Dashboards? (click to expand) I upgraded my basic Asana instance to the business instance (in the trial) in order to see what I was missing out on. As I read through the doc's, I came across Dashboard's. The functionality sounds good - an ability to view status of multiple projects, like you might expect from a portfolio, with some level of analytics. However, no matter how hard I tried, I couldn't find this page. Even clicking the direct link took me to portfolios. ![](https://www.bentumbleson.com/content/images/2019/03/image-3.png) This link didn't work as expected. It's not clear to me if this is new functionality that isn't fully rolled out, or depreciated and not removed from documentation. Regardless, it seems like a great step towards adding executive visibility options to Asana, but it hasn't made a full debut that I can investigate. It is very cool though! --- Asana includes functionality called "Progress", meant to act as a way for project managers to provide timely updates on their projects in an exceptionally concise manner: ![](https://www.bentumbleson.com/content/images/2019/03/image-4.png) "Progress" status update in Asana. Simply provide a short write-up with the latest status, and select an overall status for your project. It's deceptively simple, includes options to "remind" users to update on a weekly basis, and has great integration throughout the app, including on the project itself as a sidebar card, and in a weekly status email for portfolios of projects. ### Define: Progress v2 To make it clear: I love the existing options, but most companies have structure in place that would make it difficult to truly adopt this and find value. The interesting part here is that the help text provided (*"What's been done? What's next? What's blocked?"*) is a really good approach for a status report. Let's break those down quickly: - ***What's been done?*** \- In project management terms, what are the latest accomplishments of a project? These accomplishments are likely just the status of upcoming milestones. Why can't this form pre-populate recently completed milestones (or tasks) here? - ***What's next?*** \- Another easy one: what are the upcoming milestones for this project? Can't this information be pre-populated if the project plan has been built correctly? - ***What's blocked?*** \- I liken this most to risks and issues. If a task if off track, you put milestones at risk, and thus the project. Risks to a project can either be mitigated or turned into issues that need to be resolved. Since Asana has no formal ability to track risks/ issues, the only place they ever become visible is if a project manager lists them freely on this list. While I like this list, you can start to see how it can be improved. To take it a step further, we might explore what else good status reports keep updates on: - **Overall** \- Asana nails this with the overall indicator. However, imagine if you had *other* indicators that rolled up to the overall one based on rules? - **Schedule** \- This is another way of saying *is the project on track?* Risks and issues can impact schedules, but changes to scope, resources, and even unforeseen events (such as employee sick days) can threaten a project's schedule. - **Scope** \- While specific to larger, less defined projects, sometimes it's easy for projects to balloon in scope by adding/ changing requirements. Being able to assess the scope of a project in real time is valuable in understanding the health of a project. - **Risks/ Issues** \- If there are formally raised risks or issues, this is a good time to disclose them. Leadership want to know what's blocking a project and what they can do to help. - **Resources** \- Are project resources available and working as expected? Lengthy and large projects can suffer from resource availability for a host of reasons including PTO, vacations, sick days, jury duty, or even other project responsibilities. - **Budget** \- Good projects are completed on time and on budget. If a project works on a budget, wouldn't you like to know how you're trending each week? - **Accomplishments/ What's Next** \- Something discussed above and something Asana already requests through free entry, but can easily enough be auto-populated with milestones of certain criteria. --- ## Design (fit): The Pyramid of Clarity Jackie Bavaro, Asana's head of product, regularly provides insights into product-specific components of Asana on her blog. While I've enjoyed reading many articles she's posted on their PM blog, there's a specific article which gives insight into [how Asana builds their product roadmap](https://medium.com/@jackiebo/how-we-build-our-product-roadmap-at-asana-56953b1e25ad?ref=bentumbleson.com). What I particularly appreciate about the article is the breakdown where Jackie explains the difference in approach between the two companies she's worked at previously (Google and Microsoft). She uses the two companies as examples to show how strategy and execution of work differed, and how Asana works to strike a balance between these two approaches. Ultimately, Jackie explains the concept of "The Pyramid of Clarity": ![](https://www.bentumbleson.com/content/images/2019/03/image-2.png) **Source**: Jackie Bavaro, [*How We Build Our Product Roadmap at Asana*](https://medium.com/@jackiebo/how-we-build-our-product-roadmap-at-asana-56953b1e25ad?ref=bentumbleson.com) I would encourage you to read Jackie's explanation of the pyramid, as it gives a lot of context to how ideas are structured. Jackie gives one example about how a challenge is routed from the bottom up, relying on the relatively more fixed objectives, strategy, and mission of near the top of the pyramid. I've transposed the example into a simple flow: ![](https://www.bentumbleson.com/content/images/2019/03/Screen-Shot-2019-03-10-at-5.48.00-PM.png) As Jackie explains in her post, this is the "mapping" a challenge or new idea would fit into. I've materially simplified the idea, but it remains consistent and inline with the *pyramid of clarity* from above. --- Using Jackie's methodology, with the weakness I described above (executive value/ status reporting), I created a similar flow for understanding how (and if) this challenge fits the companies overall mission: ![](https://www.bentumbleson.com/content/images/2019/03/Screen-Shot-2019-03-10-at-5.48.11-PM.png) ### Challenge: how to make Asana more valuable to leadership? If we think about where Asana fits in the landscape of other collaborative work tools, Asana exceeds in the execution component of work - the lowest level of execution. However, one area that is particularly underdeveloped is the reporting or status component of that work. When you consider the stakeholders at each level within an organization, there's varying "needs" of reporting, or status: - **Team Member:** how many tasks are on my plate today? Is there anything preventing me from doing my job? - **Project Manager:** are any tasks slipping? Risks/ issues? Are we at risk of missing deadlines? - **Portfolio Manager:** which of my projects are on track/ off track? Why? Budget/ resource/ schedule/ scope/ etc. Do they remain inline with the portfolio strategy? - **Executive:** is our investment proportionally delivering the expected value from the underlying portfolios of work? With Asana's currently available status capabilities, project managers are left to largely manually report status in an unstructured manner (free text), with a single overarching identifier of a project - are we on track, at risk, or off track? *(more detail on this later)* I particularly love the simplicity of this approach. However, I can't think of one Fortune 500 client I've worked with that would have executives appreciative of this approach. Lacking structure is both a great strength of fluid organizations, and a tremendous weakness to larger ones that rely on standardized approaches to keep projects moving. So the question still remains, how we do we leverage all of the data Asana is producing and present it in a manner that is valuable all the way up the chain of leadership at any organization. --- ### Key Result: more comprehensively report project status While this isn't quite as quantifiable as doubling loading time, you can quantify the value of the existing "progress" functionality as a component of its usage. Some questions I would ask that could be baselined and measured: - What percentage of organizations actively utilize "Progress"? - What percentage of projects actively utilize "Progress"? - How often are status reports printed? - Of projects that use "progress", what is the average time between each update? - What percentage of users have enabled/ disabled the "remind me" function of status reporting? - What percentage of users have a title of "Manager", "Director", "Vice President", or "Chief" in their title? (*it's my hypothesis that many of these levels do not directly receive value from Asana as it is today, so I'd expect this to be low, but grow if you can show value)* All of these quantifiable numbers point to ways that you can deliver more value to a broader group of users. If you don't make a team members job any harder/ more complicated, but you enable more visibility and transparency into the status of a project, you open up a whole new level of interaction with Asana. --- Sidenote: Asana License Types (click to expand) As a small side note, Asana currently doesn't differentiate with "types" of users. Billing is standard between any type of user - whether they are a team member, actively tracking tasks, or a project manager, who oversees progress. Finally, any level of executive is treated just the same as a team member, which at many organizations, is overkill. Perhaps a more broad challenge should be how Asana can broaden the scope of how they define a "user"... this might be changing their pricing model, however. I suspect there's a good chunk of this that already happens at the "enterprise" tier. --- ### Objective: Increase product depth I would imagine that at an Executive level at Asana, they are hard at work trying to figure out how to grow their share of the market. Many great products make reasonable expansions as they begin to mature in their original offerings, and it's not unrealistic to think Asana has started conversations on the topic. For example, I had an opportunity to work with the product team at Clarizen (on behalf of a client) as they tried to build out the financial management capabilities of that tool. In the same vain, I can see from Asana's pricing page that resource management is coming soon. As a primary component of project portfolio management (PPM), I would expect that Asana is already in the mindset of finding ways to expand the depth and breadth of their tool. As such, I would reasonably conclude that expanding the capabilities of Asana's status reporting (progress), would be in service to a company objective to increase the range of clients Asana can be used by, as well as a product objective to increase the capabilities of the tool to better support existing customers. A win-win. --- ### Strategy: Make a product for all teams This one is a little more straight forward that the last as we higher up the pyramid. Jackie describes in her post three primary "strategies" Asana works towards: 1. Make a product that makes teamwork effortless 2. Get that product to all teams 3. Make Asana the best company at doing 1 & 2 In the context of ***increasing product depth***, I would argue this is in service to ***2) get that product to all teams***. "All Teams" is just broad enough that I believe it covers the vertical hierarchy of teams, much more so than different execution teams (e.g. sales, marketing, IT, engineering, product, etc.) So if you're designing new functionality that is specifically designed to enhance existing capabilities to deliver more value to project managers and executives, then you're most definitely contributing to the strategy to make a product for *all* teams. --- ### Mission: Help humanity by enabling effortless work I won't dwell on this once this mission and strategy are pretty fixed at Asana, and already fit nicely together. Since we've justified how our enhancement fits a key results, serves an objective, and aligns with a defined strategy, then I think we can agree this we are in service to Asana's broader mission. --- ## Develop I think there's a hundred different ways to solve this problem. The great part about working with teams where everyone is as dedicated to the product as you are, is you get a rich dialogue and discussion around any type of change, no matter how small. While I'd expect any such brainstorming/ design session to include a wall of sticky notes, sketches, and wire frames, I jumped a bit ahead and threw together a few simple ideas. I won't back these as the best approaches, but something to start a conversation. ### 1) More input detail ![](https://www.bentumbleson.com/content/images/2019/03/asana_mockups_1F2.png) In this mock-up, I've added stoplight "indicators" to the many categories of a status report I described in the above section. While we've made a project managers job a bit more difficult, you're allowing them to quickly generalize the status of a project against a range of criteria, and you're doing it in a tangible, defined manner. Some other considerations: - What if the list of project attributes was customizable? Perhaps not all projects are concerned about scope, or budget. Could be simple enough to drop those fields. - You could auto-populate the stoplights based on last weeks selections, or make automatic selections based on other data. For example, if a certain amount of tasks are past-due, then "schedule" might be yellow/ red. - Have new tasks been added recently by a non-PM individual? Perhaps the scope is yellow. - Ultimately you can use the stop lights of this criteria to automatically determine overall status. --- ### 2) Status from a glance ![](https://www.bentumbleson.com/content/images/2019/03/asana_mockup_4F.png) Now that you've captured weekly detail on each of these items, you can show them right inline on the existing status report. In this mockup, I'll left the free text field for entering anything, but you can see icons relative to each of the items above. In this screenshots, risks/ issues is yellow and resources is yellow, but the overall project is still green. As an executive, this is infinitely more telling (more visibility!) about the health of a project, even though it's still green. If the project suddenly goes yellow next week, that same executive won't be caught off guard and can quickly pinpoint the issue, without ever having to read a sentence. This is especially valuable because that free-text has no rules. The "*current accomplishments"* and "*what's next*" I've written above is in no way standard. Imagine the headache of a executive who has to read a dozen different summaries all formatted differently. ### 3) Risks and Issues as Objects ![](https://www.bentumbleson.com/content/images/2019/03/asana_mockup_5F.png) While Asana doesn't have a published data model, I'd venture that it's actually pretty simple: portfolios, projects, milestones, tasks, users, files. In theory, this hits all of the necessities for work management, but there's some notable items missing, specifically for projects: risks, issues, action items, and decisions (RAID). In the mockup above, I've transposed risks and issues onto the project main page as sidebar "objects". Tracking risks and issues as their own records enable even basic levels of tracking, including assigning owners, mitigation dates, severity, and more. That information is now immediately actionable (trigger emails, promote follow-up), reportable, and trackable. If projects can have templates to get started, doesn't it seem realistic that similar projects might suffer from the same risks? Tracking these allows such a deep level of visibility and reporting, you can prevent any project in the future from battling the same issues again. --- ## Conclusion Asana is a beautiful, collaborative work management tool that supports work execution well, but offers little functionality that provides strategic insight. While this isn't necessarily Asana's goal, I believe there are opportunities to mature product functionality (such as project status reporting) that would make it more valuable to leadership and executives that drive and support project work and execution. In doing so, Asana has an ability to become a more full featured product that works more comprehensively across a business, and cements itself even further as a critical business asset. ### Cards in Web Design: Balancing Simplicity and Value URL: https://www.bentumbleson.com/cards-in-web-design-balancing-simplicity-and-value/ Last updated: 2022-10-24T04:09:22.000Z ![](https://www.bentumbleson.com/content/images/2019/03/Screen-Shot-2019-03-24-at-7.33.22-PM.png) **Photo**: [Tailwind CSS](https://tailwindcss.com/docs/what-is-tailwind/?ref=bentumbleson.com) "Cards" are a fantastic, somewhat recent, and time-tested trend in web design that come with a host of benefits that has contributed to this trends proliferation throughout the web and application design. While I won't claim to be a seasoned web design expert, I know enough to be dangerous and recently worked through a "card" redesign that warranted sharing. ### Cards? Perhaps without even realizing it, you've been using cards for quite some time each time you access your favorite sites and apps. Any element that acts as a "container" with its own "record" is considered a card. Any data type that can have multiple records is a good candidate for a card. For example, comments, contacts, status updates, just to name a few. Generally any plural noun can make a card. Without diving into the details, there's a few key benefits to cards in web design: - Self contained, clear separation of data - Increased operability of data (cards can be "acted on") - Very scalable; use the same design across desktop and mobile - Better space utilization; cards can help favor vertical length on mobile devices In my case, I'm working with financial data, namely stocks, or stocks within a portfolio or watch list. ![](https://www.bentumbleson.com/content/images/2019/03/home_small.png) One of my early mockup for Carbon... perhaps too simple. Above is a mockup I did for Carbon awhile back. Around the time I was designing this, Robinhood was starting to take hold amongst millennials, and I think for the first time in a long time, they had made finance beautiful. Not looking to invent the wheel, I set out to design *the simplest* finance platform ever. Even children can understand the concept of the stock market, so why is it that current offerings provide you tools that are literally overkill for what the first time investor needs. It was this premise that guided my early design. My thought being, how simple can I make it to view my portfolio and my watchlist? I started by designing some header cards (see the "My Account" tiles), then tiles representing stocks, using swim lines to indicate their ownership (watchlist versus portfolio). While your first thought might be how *little* information is actually visible on this page, there was one hidden trick: each of the cards could be "flipped" on a mouse hover to reveal a backside. In the case of stocks, there was a little more detail about the stock, the current price, daily change (%), and a quick chart/ sparkline: ![](https://www.bentumbleson.com/content/images/2019/03/2019-03-24-11-03-52.2019-03-24-11_12_38_small.gif) Demo of the card "flip" I originally designed. Nifty right? Unfortunately "cool" doesn't translate to great usability. Here's why this was a bad idea: - Relying on "hover" to show more detail is a bad idea since it limits the usability primarily to desktops. Yes, you can trick mobile devices to do "hover", but the experience is never as smooth. - I'm purposefully increasing the work a user must do to view their own information. What value is a simple logo on the front side? - Since each card is self-contained, there is no way to view the status of multiple together, defeating any "at a glance" value. --- ### What information do I need? The driving premise behind my redesigned tiles was to revisit some of the core tenants of UI/UX design: who is using this, why, when, and where? This is pretty straight forward: anyone using the application to view the status of their current portfolio of stocks, as well as the status of those in a watchlist. These cards will make up the "homepage" and will be one of the most visible elements of the entire application. > What information do I want to see for a stock tile? ### Core Elements - **The stock name/ logo.** This might as well be the "primary key" for the card, and consistent with one of the missions of Carbon (make finance simple), I strongly believe adding visual identities to the stocks (e.g. logos) make them far more approachable. Most first time investors aren't buying faceless ETF's or bonds, so the scenario wherein a logo doesn't exist is rare. - **The current share price.** This is one of the core things you'd want to see about a stock, regardless of if you own it or are watching it. - **Percent change.** In many cases, just knowing the price isn't good enough - you really want to understand the change from the last price so you have bearings about if the stock is doing well or not on that day. Finance typically represents this change as a percent from the previous days close. ### Secondary Elements - **Current position (dollar or percent).** If you own a stock and it's in your portfolio, you might be most interested in knowing what it's currently worth. Since "value" is equal to shares owned \* current price, then showing the actual value allows you to forgo needing to show this math. Similarly, a percent change (while arguably the most valuable from a scannablity perspective), suffers from not having a consistent frame of reference. This is pretty easily overcome by doing a percent change from the previous day, but since (as first time investors), we don't want to unnecessarily scare our users, this percent change could be very misleading *(e.g. a stock down 5% one day but up 10% for the week is still doing great, but you might not know that if you only saw that one day decrease)*. - **Change over time.** Stocks move a lot. Just like I explained with viewing a percent change over a single day, investors may wish to know how a stock is doing over a broader time horizon, such as the past month, or year-to-date. This element is actually the most detailed since it's no longer one data point, it's many - a time varied series likely manifested as a chart. --- ### Version 1 As it turns out big logos aren't really necessary, so I took my tile and broke it into thirds. Additionally, based on the above, I know there are really two different tiles I'm trying to design, but it would make sense that they share many of the same elements. If you owned the stock, and thus is was in your watchlist, it might look like this: ![](https://www.bentumbleson.com/content/images/2019/03/portfolio.png) - **Left**: The visual identify I spoke about above. I assume if you *own* the stock, you very likely remember buying it, and have some sort of vested interest it. As such, I'd expect that after doing your research to purchase the stock, you'd be able to consciously match the logo with the company, without a name. - **Middle**: Although this is a "secondary" element, it's arguably more important that the current price, since the current price is directly proportional to your value (equity) in the stock. - **Right**: showing the "total return" as a dollar change really helps to add a tangible element to the ownership of that stock. Instead of knowing you're up 10%, a first time investor might take more satisfaction and derive more value in knowing they were up (or down) by a certain dollar amount. Adding color and a chevron helps add depth and scannability to this tile. Similarly, the tile would change if it was in your watchlist (meaning you don't own it). There are a few small distinctions: ![](https://www.bentumbleson.com/content/images/2019/03/watchlist.png) - **Left:** A visual identity is just as important so the stock logo remains on the far left. - **Middle:** Since you don't own this stock, and some companies don't have immediately recognizable logos, I felt it important to include the name of the company (stock), with its ticker symbol. While the ticker symbol (e.g. BMY for Bristol-Myers Squibb) isn't actionable, it helps bring some common finance elements to the surface. - **Right:** Finally, we can pair together the other two core elements: current price and percent change. Using a little color for the change and a simple chevron helps with scannability. --- ### Version 2 I'd like to focus on the stock tile that appears when your own a stock (in your portfolio), or my first graphic above. Of the elements I described above, I'm currently missing the current price, a percent change, and a change over time. Since the last element necessities a chart, I can't really rely on the natural "thirds" separation. What if the chart got two thirds and I stacked the remaining elements like I did previously? ![](https://www.bentumbleson.com/content/images/2019/03/Screen-Shot-2017-11-04-at-3.58.40-PM.png) Here I added a sparkline chart and added the current price with a percent change (*just as I did in the watchlist tile*). I also added a very slight gray "status bar" between the two elements. In this case, it's at exactly 50%, but I foresee this working well as a visual indication of time left in a review. Don't worry if this doesn't make any sense in the context of this; just think about if you knew you wanted to revisit your ownership of each stock, say every 90 days (say you wanted to rebalance, reconsider position, etc.). This simple progress bar would count down, so without even seeing a number, you could quickly know how long it's been since you last revisited that stock. I like this iteration, but the sparkline is somewhat weak. The time series isn't clear (X axis), nor is the actual variance of this stock (Y axis). I can reasonably assume the last value (far right of the line) must be equal to the current price, but I have no concept of what other points on the line are. --- ### Version 3 In this iteration, I added scales, and a simple grid. This same line is immediately much more valuable (*disregard the dummy data*). If you're curious, I'm using [Chart.js](https://www.chartjs.org/?ref=bentumbleson.com) as my charting library for this. While I never wired up any real dynamic data, I did code the look and feel of this chart, including the subtle fading of the grid, under the curve, and the feel of axises. ![](https://www.bentumbleson.com/content/images/2019/03/Screen-Shot-2017-11-13-at-10.15.12-PM.png) Disregard the fact the Y-axis is between $100 and $95 and the "current price" is 141.69\. This is all dummy data. This still is missing one thing - what if I want to change the time scale of the chart? ![](https://www.bentumbleson.com/content/images/2019/03/Screen-Shot-2019-03-24-at-5.18.31-PM.png) Since vertical space is much more plentiful that horizontal, it's easy enough to tack a few time-series buttons on here. --- ### Clean-up the Watchlist Tile Last on the agenda is to bring some of this same thinking with the chart to watchlist tile. Although in this case, we're talking about a very different type of stock here. While a user's portfolio will likely stay under eight stocks, or two rows with four each (and I think even that is high for the first time investor), a users watch list could easily hit two or three dozen. Can you imagine a page with seven or eight rows of charts? What if we collapsed the chart under a drop-down arrow? ![](https://www.bentumbleson.com/content/images/2019/03/Screen-Shot-2019-03-24-at-6.47.54-PM.png) Now we get the same benefit as the full portfolio tiles, but we can tuck it away under a simple carrot, revealing only when needed. As an added touch, I've added a top border to the tile so it's even more immediately scannable as to that days status. Here's that whole process: ![](https://www.bentumbleson.com/content/images/2019/03/2019-03-24-19-12-29.2019-03-24-19_15_05_small.gif) This approach has the advantage of being to show a lot of tiles, without feeling too cramped. Here's a quick mockup with a watchlist with 14 different stocks (disregard the dummy data on the numbers): ![](https://www.bentumbleson.com/content/images/2019/03/Screen-Shot-2019-03-24-at-7.09.17-PM.png) And bringing it all together once more: ![](https://www.bentumbleson.com/content/images/2019/03/carbon_v2_F_small.png) Carbon "v2" mockup ### Human Capital: Assets or Liabilities? URL: https://www.bentumbleson.com/human_capital_assets_or_liabilities/ Last updated: 2022-10-24T04:10:43.000Z ![](https://www.bentumbleson.com/content/images/2019/03/chessfactor_dribble-2-1.png) **Photo Credit:** [Ivana Petrovic](https://dribbble.com/ivona%5Fpetrovic?ref=bentumbleson.com) (Dribbble) ### Executive Decision Making Let me start with a question: Imagine that you are an executive of a manufacturing company in any industry with a physical product (think automotive, agriculture, technology, consumer goods, etc.) As an executive, do you treat your own product inventory as an asset or a liability? It's a profound question, and in the context of manufacturing, represents a business decision grounded in metrics: demand, shelf life, production lead time, to name a few. There are famous examples of companies like Toyota or Dell treating assets as liabilities and materially altering how they operate in search of a better bottom line. These are specific examples to manufacturing, but the core concept remains consistent. --- ### You're A Risk Shifting the discussion to human capital; are you an asset or a liability to your company? Some companies, especially service-heavy ones (such as consulting), are known for their "*people are our assets*" mantra that extends throughout their culture. Counterproductive to that culture and vision, execution doesn't always match - especially so in global firms where there is a tremendous amount of risk to consider and mitigate on a macro scale: I may be an asset to my company based on the value I provide, but I'm still a liability with the information I'm privied to. I suppose there's a good reason my computer runs virus scans all the time, and is indefinitely connected in real time to my company's IT department (*thanks Tanium*). I additionally have restrictions regarding my usage of asset managers and brokerage firms, specifically because my company needs visibility into my holdings to make sure I'm not doing (or owning) anything I shouldn't be (think insider knowledge). This is a preemptive measure to control risk, but is it necessary? Is training and trust not enough? I'd like to believe that it took just one person to ruin it for the rest of us. Is the reason small companies do not have these level of restrictions because they haven't encountered these high risk scenarios, or they have more confidence in the training programs to deter this activity, rather than prevent it altogether? At its core, I understand why it's done. Many employees leave the door open for risk at scale, and it should be mitigated within reason. I do wonder how many freedoms must I give up in the name of security and safety? If this sounds familiar, it's because this same concept of corporate risk management extends to governments (more on that later). How many decisions have been made under the blanket of national security? --- ### Who's Problem? I recently watched a recorded lecture of "*lessons learned in career of product management*", and a consistent message throughout the presentation was that product managers should be designing solution for their customers. Rephrased, solving your customers problems, not your own. The example presented was Evernote's switch to a freemium model, which highlighted Evernote's short sighted thinking to solve its own problems (profitability), versus that of their customers. So when companies, organizations, or governments implement policies designed to control their risk, do they have you (the customer) top of mind? Are they solving your problem or their own? --- ### Side Note: Government Risk Does the US government view its citizens as assets or liabilities? I'd argue in the US, you're an asset until you've proved otherwise (undermined the inherent trust placed in you to do the right thing and contribute to society). Do government laws and restrictions make you a liability? Youth curfews, driving restrictions, and drinking age requirements are examples of helping control and mitigate risk, but it undoubtedly shifts the scale of your value away from that of an asset (though these examples are specific to minors, there are examples across the board of age). In China, are citizens assets or liabilities? If China's "social credit" system is any part of a case study, I would speculate that China views citizens with an unhealthy level of skepticism that artificially controls the behaviors and actions of citizens (*read: this is communism*). It's one of the world's greatest examples of risk mitigation: implementing so many levels of control and oversight that I'd be hard pressed to ever understand how such levels of control (dare I say censorship) could be alleviated. --- ### For your consideration Are you an asset or a liability to your company? Why? What decisions were made for your benefit versus that of your company? I think we all could benefit from asking why I little more, and I think we all deserve to work for a company that truly sees us as assets, not liabilities, at any scale. As a final point which precipitated this thought: one piece of advice that has particularly resonated with me as of recently suggests that you "change the lens" of the problem you're trying to solve. Sometimes the solution you see from the outside was never designed to solve the obvious answer. ### "Vision" - A study of a new Plaid Product Offering URL: https://www.bentumbleson.com/vision-a-study-of-a-new-plaid-product-offering/ Last updated: 2022-10-24T04:14:14.000Z ![](https://www.bentumbleson.com/content/images/2019/01/plaid_graphic.jpg) ## Plaid: The Backbone of Finance [Plaid ](https://plaid.com/?ref=bentumbleson.com)is a wonderfully crafted, and beautifully executed service that acts as the backbone for some of the largest FinTech companies of this decade. Their portfolio of API-driven solutions, materially reduce friction and allow FinTech companies to better connect with their clients. Odds are that if you've used any modern financial application in the past few years, you've probably used a service built on Plaid's infrastructure without even knowing it. Plaid has a suite of products that work together to solve common issues seen across various applications of financial technology. The graphic below illustrates Plaid's current product offerings and how they align to a range of use cases in finance. Here's a breakdown of their six products: - **Transactions** \- Access detailed transaction history and balance data - **Auth** \- Instantly authenticate bank accounts for ACH and EFT payments - **Identity** \- Verify users’ identities and reduce fraud - **Balance** \- Verify real-time account balances - **Assets** \- Verify borrowers’ assets straight from the source - **Income** \- Understand income and verify employment ![](https://www.bentumbleson.com/content/images/2019/01/plaid_graphic_v2.png) Read all of the details on [Plaid's website](https://plaid.com/products/?ref=bentumbleson.com). | Graphic made by me with use of Plaid assets. One thing that Plaid does phenomenally well (especially following their [rebranding effort](https://blog.plaid.com/behind-the-scenes-with-design/?ref=bentumbleson.com)) is paint a very clear picture of how each product solves a real problem in finance: issue, solution. It grounds the complexity of FinTech into palatable solutions that drive benefit and impact. --- ## Finance is not "One Size Fits All" Plaid's product line is comprehensive in its ability to solve a range of use cases. However, there is a subtle lack of specificity to *how* it solves those problems. More specifically, financial products and services are all designed with a specific consumer in mind - mortgages are for homes, 529 savings plans are for parents saving for their children, IRA's are for everyone who wants to retire, etc. In the era of modern FinTech, where face-to-face with a financial planner isn't as valued as it was in the past decades, there is still a need for personalization. And in a market of increasing offerings, there is heightened sensitivity to matching consumers with what suits them best. **Issue**: Finance is not "one size fits all". There are a host of products and services that support a range of consumer and enterprise financial applications. However, the benefit to clients can be hindered by too large a portfolio of services or offerings. It can also strain customer care, sales, and product teams who are trying to match the right product to the right customer. --- ![](https://www.bentumbleson.com/content/images/2019/01/vision_intro.png) **Solution**: Plaid derives, prepares, and collates relevant demographic, socioeconomic, and psychographic detail to allow for detailed segmentation of consumers to better target offerings, services, and overall reduce friction in any range of financial offerings. Note: This isn't a real product of Plaid, just a mockup of one. --- Just like census information is used for better planning, targeting, and forecasting of government assistance programs, *knowing* your client is an easy first step to unlocking a range of customized offerings that can lead to better user retention, higher product utilization, and fewer support requests. Making financial products more targeted and more available leaves everyone better off. **Demographics:** - **Age**: knowing how old a client is, even if it's just a range, can be immensely useful in building risk profiles for investments, creating time horizons, and understanding financial priorities. - **Life Stage:** As with age, understanding the life stage a client is in can help determine priorities and what products or services are most relevant. - **Education**: One of the biggest predictors of creditworthiness and wealth, simple classification of clients by education can help make better lending decisions and promote the usage of correctly targeted products. - **Relationship Status/ Family:** While seemingly personal, this information is a good indicator of the types of financial products a client might need, including 529 college savings plans for kids, mortgages for new homeowners, IRA's for new graduates, etc. This can also help to approximate tax liabilities in joint versus individual filings, dependents, etc. - **Profession:** Although Plaid has a host of products that can determine income and the expected consistency of a paycheck, understanding a client's profession, even at a general level can help piece together risk profiles for loans, or otherwise recommend relevant products (e.g. recommending a 401(k) to a food service employee is poor targeting). - **Lifestyle:** Urban, suburban, and rural living are all representative of very different ways of living. Using historical and actual addresses can help build better risk profiles, product targeting, and support channels to meet varying lifestyle's. **Socioeconomic Factors:** - **Income:** Very easily derived from other Plaid products, a client's income might as well be one of the driving factors of creditworthiness. - **Education:** As with the demographic classification, education plays a role in income, occupation, and even likelihood of home ownership. - **Occupation:** Very similar to profession as described above; used for better risk profiles and recommending relevant products and services - **Wealth**: Derived from Plaid's other products, getting a holistic view of assets (especially over time), combined with age, marital status, family size, occupation, and education are all predictors for wealth attainment. This can be used to target products that provide an ability to meet emergencies (liquid assets), absorb economic shocks (sustainable investments/ lifestyle), or provide the means to live comfortably (retirement planning). **Psychographic Segmentation:** While commonly associated with political campaigns, psychographic segmentation is a powerful tool, though difficult to derive, to segment clients based on their **activities**, **interests**, and **opinions** (AIO variables). In marketing, onboarding, and retention, correct targeting could mean the difference between lifelong customers and high attrition rates. The caveat is that collecting psychographic information is a careful process. Deriving it can be difficult, especially without skirting moral boundaries of data analysis. Cambridge Analytica is a great example of a company that provides psychographic segmentation, but they've done it in a scrupulous manner that drew harsh scrutiny. On a simpler level, most psychographic segmentation boils down to an alignment of personality traits in the "[five factor model](https://en.wikipedia.org/wiki/Big%5FFive%5Fpersonality%5Ftraits?ref=bentumbleson.com)": - **Openness to experience** (*inventive/curious* vs. *consistent/cautious*) - **Conscientiousness** (*efficient/organized* vs. *easy-going/careless*) - **Extraversion** (*outgoing/energetic* vs. *solitary/reserved*) - **Agreeableness** (*friendly/compassionate* vs. *challenging/detached*) - **Neuroticism** (*sensitive/nervous* vs. *secure/confident*) While I won't question the difficulty of gathering this information (accurately), especially without asking or taking a quiz, there are opportunities to extract and analyze data (with permission) to help tailor offerings. For example, a client that allows a Plaid API to scrape their Facebook, twitter, and Instagram profile might be able to build a simple profile based on their activity. Someone who is young and "curious" might be more receptive to new investment opportunities with a brokerage account, or may be more likely to use a nontraditional mortgage product. This is just an example. Psychographic segmentation should be a long term goal that builds on the segmentation capabilities of demographic and socioeconomic factors as described above. In many respects, it can be considered a form a micro-segmentation - allowing FinTech companies to dial in their targeting efforts even better between clients who may look identical on paper (demographically), but react very differently based upon their personalities. --- ## Plaid "Vision" - Use Cases ![](https://www.bentumbleson.com/content/images/2019/01/plaid_graphic_w_vision_v1.png) At its core, **Vision** provides deeper insights into the clients utilizing various financial products, which make it a great addition to personal finance, lending, and banking/ brokerage applications. In the sections below, we'll do a smaller deep dive on how each use case can benefit from the deeper insight and *vision* of this product. --- ## Personal Finance ![](https://www.bentumbleson.com/content/images/2019/01/Vision_detail_1F.png) Personal finance has historically, always been personal. While there's nothing wrong with this, the socialization of finance has had some interesting (and largely positive) effects that work in the benefit of the consumer. For example, you'd be surprised by the amount of articles explaining how much you should have saved by XYZ age. Everyone wants to know how they stack up against those like them. Am I financially better off than those my age? Am I saving more than my age group? Am I spending too much money on restaurants compared to others like me? Am I spending more money online shopping than others like me? There's an almost limitless combination of segmentation you can provide by matching personal finance habits with the anonymously segmented metrics from demographically similar clients. And the best part, this isn't new. Companies like Mint or CreditKarma have plenty of "insights" littered throughout their applications allowing you to compare your spending or credit score to others like you. --- ## Lending ![](https://www.bentumbleson.com/content/images/2019/01/Vision_detail_2F.png) The practice of lending has always been grounded in the creditworthiness of lenders and the risk associated with certain arrangements. While a users credit score is a powerful number that takes into account many financial metrics, it's inherently limited in some applications. One such application is lending for Millennial's/ Gen Z whom may not have well established credit profiles. How else do you determine the default risk of a loan with limited information? Turns out the Government solves this problem every year when Federal Student Loans are determined, and they've figured out that [loan-delinquency rates differ sharply across demographic groups.](https://www.stlouisfed.org/~/media/files/pdfs/hfs/assets/emmons-ricketts-demographics-of-delinquency-june-1-2016.pdf?la=en&ref=bentumbleson.com) Adding demographic information to lending profiles can help better calculate risk and default rates. Since we know that demographics are good predictors of credit worthiness, especially when looking at "good" loans, like federal student loans, or scenarios where applicants do not have largely established credit profiles, utilizing demographics can help piece together a more holistic profile to make better decisions. This also has the added benefit of reducing friction since you don't have to ask for additional information that may help users get through setup or on-boarding quicker and allow lending companies to provide decisions sooner. --- ## Banking and Brokerage ![](https://www.bentumbleson.com/content/images/2019/01/Vision_detail_3F.png) Banking and brokerage is on the forefront of financial modernization: we've made leaps and bounds in democratizing access to markets and bank accounts. There's still the question of education, or financial literacy in general, but you can imagine that if you're putting the right products and services in front of the right consumers you'll get better conversions. Add demographics to client profiles makes it easy for product placement, especially in the brokerage space. For example: IRA's are great retirement vehicles and should be encouraged to those who are perhaps opening their first account and 529 college savings plan are great for new parents. There's a web of combinations that allow better targeting here that signup screens with a range of products and services that might not be well targeted and may serve to confuse rather than help. Finally, as an auxiliary benefit, you now have an ability to provide support options that make most sense to the consumer you're targeting. For example, [Millennial's prefer self-service, messaging (texting) over calls, want fast help, and an experience that is personalized to them](https://www.salesforce.com/blog/2017/08/how-millennials-are-redefining-customer-service?ref=bentumbleson.com). Think about how support happens in the Uber app versus Edward Jones. Which is more friendly for Millennial's? Gen X? Baby Boomers? If you *knew* how your client wanted to be helped, and in a manner they would find most helpful, you could redeploy human capital and technology assets to more efficiently use time, resources, and money. ### How daylight savings time impacts international meetings URL: https://www.bentumbleson.com/how-daylight-savings-time-impacts-international-meetings/ Last updated: 2023-11-19T00:35:11.000Z ![](https://www.bentumbleson.com/content/images/2018/12/image-10.png) While I won't directly comment on why daylight savings was created, and how it's still justified today, there is an ongoing concern of how daylight savings time affects those of us that regularly work across time zones. Especially in an increasingly connected world, it's easier than ever to work across borders and seas with little interrupt. In fact, technology has made the task of tracking daylight savings time significantly easier. I regularly have meetings with counterparts across the global, most specifically in India. Since India is almost a full 12 hours across the globe, a typical meeting time for us is early US morning, late India evening. Culturally I found in my own visit to Mumbai that most workers that work with colleagues across the globe don't show up to work until 10AM or even later, and work later into the evening to better accommodate the time zone changes. ## Time Zones 101 The mainland US has four separate time zones, pacific, mountain, central, and eastern time. Each zone is +1 hour from each other, starting with pacific time on the West coast. Coordinated Universal Time (UTC) is a commonly used timescale to understand how time zones differ from each other in a relative sense. More on this later. Here's a simple visualization of the US mainland four time zones: ![](https://www.bentumbleson.com/content/images/2018/12/image.png) Drawing a vertical line through any time will show you the equivalent time in a different time zone. Daylight savings is also pretty straight forward across the US. "*Spring forward, fall back*" is an easy way to remember the change. For example, in 2019, daylight savings will start on March 10th, and end on November 3rd. ## Daylight Savings (in the US) ![](https://www.bentumbleson.com/content/images/2018/12/image-1.png) Daylight savings is intended to provide an extra hour of sunset light, although you do sacrifice an hour of sleep when it starts. You gain that hour back when daylight savings times ends: ![](https://www.bentumbleson.com/content/images/2018/12/image-2.png) ## How Daylight Savings Affects Other Time Zones Like with currency, everyone has some "base" time zone. One that you are most likely to compare all other time zones against. For example, the UK is 6 hours ahead of Kansas (Central Time). The "frame" here is Central Time. This gets a little tricky when you consider how daylight savings affects your frame: ![](https://www.bentumbleson.com/content/images/2018/12/image-3.png) This simple line is meant to illustrate how regardless of how daylight savings has affected your time zone, the *new* times (e.g. 8AM is now 9AM) are the *same* to you (e.g. 8AM is still 8AM). So if daylight savings time hasn't shifted your time zone (side note that there is a difference between *standard time* and *daylight time*), it must have affected others that do not follow daylight savings conventions. For myself, as I work with teams based in India, I follow an approximate 12 hour time change between central time and India Standard Time (IST). ![](https://www.bentumbleson.com/content/images/2018/12/image-4.png) In the example above, you can see that CST and CDT hasn't shifted at all (recall the frame of reference), so the additional hour needs to be accounted for in other time zones. In the case of India, since we've moved forward one hour, CDT and IST are 1 hour *closer* together. > The effect of this is that meetings or any scheduled event that is based on a timezone that recognizes daylight savings, then the recipient (who operate in an area that does not recognize daylight savings) will have the meeting timing adjusted accordingly. ## Time Zones as Differences Finally, another good way to visual this change is calculating changes (delta) in time between two zones. Using UTC we can look at the difference between two zones. CST to IST, for example, is 11.5 hours. Using 9AM CST as a further example, you can see that 9AM + 11.5 hours = 830PM in IST. When daylight savings starts, CST (now CDT) is one hour less, meaning the difference between these zones is now only 10.5 hours. ![](https://www.bentumbleson.com/content/images/2018/12/image-7.png) The same is true when daylight savings ends - that one hour difference is removed, which *adds* an hour between time zones. ![](https://www.bentumbleson.com/content/images/2018/12/image-8.png) ## Conclusion If you work with distributed teams or persons that work across zones that do and do not utilize daylight savings, you should be aware of how this annual occurrence affects meetings. **First**, be aware of the regions that recognize daylight savings: ![](https://www.bentumbleson.com/content/images/2018/12/image-9.png) **Daylight Savings Time Regions**. Blue represents northern hemisphere summers, orange for southern hemisphere summers. Shades of gray represent regions that do not currently recognize daylight savings. Source: [Wikipedia](https://en.wikipedia.org/wiki/Daylight%5Fsaving%5Ftime?ref=bentumbleson.com) **Second**, know that when daylight savings starts, when we *spring forward*, others have *lost an hour.* **And finally**, when daylight savings ends, when we fall backwards, others have *gained an hour.* Luckily Outlook is pretty good of keeping track of this for us. --- *Photo Credit: [Dribbble - Aleksandar Savic](https://dribbble.com/almigor?ref=bentumbleson.com)* ### The next precision input device: touch (and pen) URL: https://www.bentumbleson.com/the-next-precision-input-device/ Last updated: 2022-10-24T04:18:05.000Z ![](https://www.bentumbleson.com/content/images/2018/11/touchinput.png) I recently watched [Apple October "Special Event" on October 30th, 2018](https://www.apple.com/apple-events/october-2018/?ref=bentumbleson.com) wherein they introduced an updated Macbook Air, Mac Mini, and new iPad's. While I have my own thoughts regarding the perhaps misaligned "fit" of the new Air and Mac Mini, I did find the new iPad's rather interesting. More and more, Apple is positioning the iPad as a computer. Disregarding their previous ad campaigns (which were arguably poor), Apple is consistently blurring the lines between iOS and macOS. While I do have some reservations about this, Apple is making an interesting stance towards the future of precision input. ![](https://www.bentumbleson.com/content/images/2018/11/Screen-Shot-2018-11-01-at-3.43.35-PM.png) Steve Jobs explaining how the introduction of the iPhone represents a new user interface - Multi-Touch. **Source:** iPhone Announcement at MacWorld in 2007. > First was the mouse. The second was the click wheel. And now, we're going to bring multi-touch to the market. And each of these revolutionary interfaces has made possible a revolutionary product - the Mac, the iPod and now the iPhone. This quote from Steve Jobs back during the introduction of the first iPhone is a glimpse into how Apple has thought about input devices over the last decade or so. While the click wheel was a very niche use case for input (scrolling menus), multi-touch (or touch in general) can replace much of what a mouse can do. Although touch-input has always been limited by desktop-class applications built to use precision inputs (like a mouse). --- ### Enabling touch input on desktop Apple has consistently held out on putting a touch screen into its Mac lineup. While they undoubtedly have the technology to do it, they've purposefully strayed away from it. Why force macOS to support touch input when iOS already does? It's an easy argument - as Apple seeks to combine macOS and iOS further, they're making a decision towards the primary forms of input that should support these. Additionally, one could argue that "dumbing down" full scale desktop-class applications to support a *secondary* input form (touch) isn't nearly as efficient as building native touch-designed applications from the ground up. Think about it: - Desktop (macOS) = Mouse/ Trackpad - Mobile (iOS) = Multi-touch/ Pen So in the future of computing, which approach is more future proof? --- ### The Microsoft Approach ![](https://www.bentumbleson.com/content/images/2018/11/image.png) Microsoft Surface Studio 2\. A creative machine designed for touch and pen input, that ships with a mouse and keyboard. **Source**: [Microsoft](https://www.microsoft.com/en-us/p/surface-studio-2/8SBJXM0M58T4?activetab=pivot%3aoverviewtab&ref=bentumbleson.com) The Microsoft approach is to let you choose. They don't force developers to make their applications touch-friendly, but they definitely encourage it. If you know that a growing proportion of your user base is using devices equipped with touchscreens, or just tablets all together, you'd be doing yourself a disservice by not enabling at least some rudimentary form of touch input. After all, Microsoft has a whole suite of touch-enabled devices with the Surface line, so they're leading the charge to touchify their OS. An important note here: this isn't about making applications useable on *mobile* (think small screens), it's about making them functional with a different form of input. Since Microsoft doesn't have a huge stake in a mobile OS (not counting Windows Mobile/ Windows Phones), it makes sense that they are more incentivized to optimize Windows 10, rather than start from the group up with say Android. --- ### The Apple Approach Apple is continuing to make the argument through product announcements that touch/ pen input is the future, and iOS is becoming increasingly more capable at replacing a desktop environment. The newer generations of iPad's even include an app dock, wholly reminiscent of its macOS counterpart. There's also assisted multitasking and the introduction of true interoperability between devices with the use of USB C. One missing piece is still a usable file browser, but perhaps Apple is purposefully leaving this out. This short list scratches the surface on the differences between these OS's, but I'll leave the rest for another day. ### iOS apps are approaching their desktop counterparts ![](https://www.bentumbleson.com/content/images/2018/11/Screen-Shot-2018-11-01-at-3.16.00-PM.png) Photoshop on the iPad Pro. **Source:** Apple October 2018 Event The most interesting part of the demo that I saw from Apple was what appeared to be a full-featured Photoshop running on the iPad Pro. The demo-er even explained that the PSD she was working with was over 3GB in size and included more than 100 individual layers. As someone who has used Photoshop quite a bit in my first few years at university (and to a lesser extent when working on slides/ presentations at work), I can attest that this is an impressive feat. I wonder what the next generation of creative professionals will create (or feel empowered to create) with this level of technology-enablement. --- ### iPad Pro vs Surface (Pro, Studio, etc) ![](https://www.bentumbleson.com/content/images/2018/11/surface_vs_ipad.png) ![](https://www.bentumbleson.com/content/images/2018/11/Screen-Shot-2018-11-01-at-4.22.41-PM.png) **Source:** [PCMag](https://www.pcmag.com/news/364703/ipad-pro-vs-surface-pro-6-which-is-the-best-laptop-replace?ref=bentumbleson.com) If you look at Apple and Microsoft's "flagship" devices, they both are billed as "laptop replacements". While PCMag (and myself) give Microsoft Surface the edge in this argument, this is strictly because it runs a full version of Windows which enables many of the workflows end users are familiar with. In fact, when you study both devices and their snap-on keyboards, you'll see that Apple's is missing a trackpad. Perhaps 'missing' isn't the right word, but when an iPad runs iOS and is wholly designed for touch-input, you simply don't need a trackpad. And I suppose that's the crux of this argument. Apple has made a bet the the future of computing won't involve a mouse at all, and as the lines between iOS and macOS merge (not to mention Apple seemingly moving away from Intel chips), I think we'll start to see a fundamental shift in how consumer devices think about input. How ironic that in 2007 Steve Jobs so vehemently spoke out against a stylus, and yet here we are in 2018 with Apple introducing their second-gen "pencil" stylus for iPad's. And while I believe Microsoft still has the edge right now in terms of functionality on their portable lineup, I would not be surprised if the next 10 years of computers show us that future doesn't involve a mouse anymore. ### Making Rocket Science Cool URL: https://www.bentumbleson.com/making-rocket-science-cool/ Last updated: 2022-10-24T04:18:25.000Z ![](https://www.bentumbleson.com/content/images/2018/10/image-1.png) Photo Credit: [Jacob Gill](https://dribbble.com/GaoJian?ref=bentumbleson.com) This last Sunday, October 7th, I tuned in to watch SpaceX's **[SAOCOM 1A Mission](https://www.spacex.com/news/2018/10/07/saocom-1a-mission?ref=bentumbleson.com)**, set to deliver a Argentinian satellite to low-earth orbit, designed to gather soil moisture information. This is SpaceX's 17th launch this year, and they're on track for at least another ten before the end of the year. While relatively mundane in terms of groundbreaking launches, I'm always in awe, glued to my screen watching almost 2,000,000 pound-force (stage 1 and 2 combined) of thrust propel a rocket so elegantly into the sky. In those moments I flash back to my elementary education, wherein *rocket science* was the vernacular for infinitely complex problems. These days - I don't doubt it. The intellectual capability that likely goes into developing rockets is mind numbing. I particularly liked how at the end of SpaceX's 15 minute live cast, they signed off by encouraging interested watchers to check out their careers page. --- ### SpaceX makes rocket launches a production Watching a SpaceX mission from start to finish almost feels like you're watching a movie. You're getting the play-by-play from a passionate engineer, you're hearing a range of engineers call out a series of validations, checks, and statuses throughout the entire launch (*and landing!*), and you're getting Hollywood-ready visuals that keep you hooked into the action. I found myself silently cheering on SpaceX through this relatively low-key launch, applauding each step as a monumental achievement, bringing SpaceX closer to some incredibly lofty goals. I even went back to review some of SpaceX's more notable achievements, like its test launch of the [Falcon Heavy](https://www.spacex.com/news/2018/02/07/falcon-heavy-test-launch?ref=bentumbleson.com), and its subsequent attempts at landing the boosters and center core. I don't think I quite appreciated what an accomplishment it was by Elon and the SpaceX team to launch such a groundbreaking rocket (and Tesla Roadster) into space. You also can't beat the sight of two booster rockets landing in perfect tandem. ![](https://www.bentumbleson.com/content/images/2018/10/25254688767_59603ff06c_o.jpg) SpaceX's side boosters from the Falcon Heavy test launch in Feb. 2018 land simultaneously on LZ-1 and LZ-2 in Cape Canaveral, Florida. **Source:** flickr.com/photos/spacex --- ### Working towards an impossible idea I think what particularly excites me about SpaceX is what they're trying to do in terms of human evolution. Their own about page literally mentions their ambitions to not only get to other planets, but enabling *living* on them. > SpaceX designs, manufactures and launches advanced rockets and spacecraft. The company was founded in 2002 to revolutionize space technology, with the ultimate goal of enabling people to live on other planets. Space travel has always been so vastly complex, difficult, and costly, that truly only governments with deep pockets could participate. It's astounding to me that someone, Elon or otherwise, would willfully put up the tremendous amount of money to even get started in the space (no pun intended). It's that deep commitment to a seemingly-impossible goal that I was always respect Elon Musk for, regardless of how he handles himself on twitter. ![](https://www.bentumbleson.com/content/images/2018/10/image.png) A render of Mars Base Alpha. **Source:** Elon Musk on twitter. --- ### To the next generation of engineers In a few words: I'm jealous. I wonder how my childhood interests and ambitions may have been impacted if I had been able to go watch SpaceX launch a rocket into space as effortlessly and as nimbly as they do now. When SpaceX did their Falcon Heavy test launch, it drew huge crowds around the launch site in Florida, and millions more tuning in online. I wonder how many kids watched the events unfold, then proclaimed to their friends and family they wanted be an engineer or astronaut. What a great way to naturally ignite a passion from STEM. I feel like the snowball in space travel and exploration is starting to move again. The "Space Race" between the US and the Soviet Union, while founded on morals of ideological superiority sparked increases in spending on education and research and development, which led to beneficial spin-off technologies, including space blankets, scratch-resistant lenses, aircraft anti-icing systems, improved radial tires, temper foam, freeze drying, and water purification, [to name a few](https://en.wikipedia.org/wiki/NASA%5Fspinoff%5Ftechnologies?ref=bentumbleson.com). I'm eager to see where we're headed next in the 21st century. ![](https://www.bentumbleson.com/content/images/2018/10/image-2.png) A screen grab from the SAOCOM 1A mission shortly after the Stage 1 boosters landed, following the Stage 2 rocket as it pushed the satellite into orbit. The quote the top comment on SpaceX's [Falcon Heavy test launch live stream video](https://www.youtube.com/watch?v=wbSwFU6tY1c&ref=bentumbleson.com), *thank you Elon Musk, for teaching us to dream again.* ### Compounding 101 - learning JavaScript along the way URL: https://www.bentumbleson.com/savings-calulator-v1/ Last updated: 2022-10-24T04:35:21.000Z ![](https://www.bentumbleson.com/content/images/2018/09/undraw_Calculator_0evy.png) As a continuation to my last post, [*Approaching the Problem*](https://www.bentumbleson.com/approaching-the-problem/), I had time recently to sit down and put "pen to paper" so to speak. As a learning initiative, I set out to build a simple savings calculator from start to finish. I'm pleased to report that I've got something up and running. Much credit to the many StackOverflow threads, Mozilla Developer docs, and some of my more qualified programmer friends for the help. Feel free to play around with it in its current state. Note that there isn't a single line of CSS (although the chart is mildly styled). You can view the full source from JSFiddle. I did have a few challenges along the way. Unfortunately I didn't document them in the moment, but here's a short list of them. **1) Converting an object to single set of values** In order to handle the calculation for each data point, there's a computed section within my Vue instance that runs a `for` loop, calculating the relevant totals and ages at each time frame. In my case, I'm storing two different `key: value` pairs, `total` and `age`. Since this is an object, I can't inherently just immediately pass that object to ChartJS and expect to be able to read it. Additionally, I need to be able to separate out the values as `total` becomes the y-axis data, and `age` becomes the labels (x-axis). It look me awhile to figure this out, and again, credit to everyone else that ever had this problem and documented it, as I was able to use `object.values` with `map` to return only one set of the values from the pair. In case you're curious, this is what that bit of code looks like: `Object.values(demo.timeData).map(function (a) {return a.total});` A few quick notes on there: 1. `Object.values` is a Javascript function that converts my object into an array, which can then be used with `.map` which can iterate though the array, returning only those values for which the key was `total`. 2. `demo.timeData` refers to the actual data that VueJS is calculating, as `demo` is the element (or `el` in Vue), and `timeData` is the computed object. 3. `function (a) {return a.total}` is the ES5 (thus IE supported) syntax of defining such a simple function to only return the `total` value. In ES6 supported browers (IE8+ and everything else), this could have been written as `(a => a.total)` --- **2) Finding data in the DOM** Sometimes I had cases where I thought I was updating the correct value, and I'd have a console.log setup to spit it out, yet when it ran, it didn't actually update the correct values. The underlying issue here is that I wasn't properly using Chart.js's options for updating chart data. In my limited understanding, I thought that if I directly updated data variable I was using in Chart.js, then called chart.update(); In my limited understanding, I thought that if I directly updated data variable I was using in Chart.js, then called `chart.update();`, it would pickup the new data and labels I was passing along as Vue recalculated the information. Unfortunately that wasn't the case, and produced this odd scenario wherein I could use the console to read `totalresult` (which was my variable for the data itself), and it'd return the original values that are calculated when the page loaded originally, but if I used `console.log` to dump `totalresult` when then `onChange` event was triggered, it would dump the *updated* calculation of the numbers. For reasons I never truly understand, it's as though the `totalresult` existed in two places, likely one inside my Vue instance, and one outside. As such, it gave the impression that my data wasn't updating as expected. Turns out, to update the data that the chart was pulling, I needed to directly update the dataset (and labels) that `myChart` had stored: ``` myChart.data.datasets[0].data = Object.values(demo.timeData).map(function(a) { return a.total }); myChart.data.labels = Object.values(demo.timeData).map(function(a) { return a.age }); ``` You can even see above the `Object.values` and `map` function that I explained in my previous point. --- **3) Ternary vs if, else statement** ![](https://www.bentumbleson.com/content/images/2018/09/Screen-Shot-2018-09-01-at-7.39.39-PM.png) Here's a screenshot of some of my original code that was used to calculate the actual data from the chart. At first glance, it made sense to me: if `$i` (an incrementing number) was 0, then `total` is equal to whatever you started with, `this.initialCapital`. However, once `$i` \> 0, then you'd need to do a calculation of the total. However, this didn't work. Console told me there was an `Unexpected token if`. After syncing with a friend of mine, he told me that I couldn't use a conditional like this, and I'd need to use a [ternary](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional%5FOperator?ref=bentumbleson.com). While I was unable to find clear documentation as to *why* the conditional wouldn't work, changing the expression to a tenery fixed the problem. I'll also mention that what you're seeing above, even changing with a ternary, doesn't fully solve the problem. Only the first value of the set returns correctly. The flaw in this approach is `this.total` inside the `total` calculation. Based on my understanding, I was trying to access the object with the data as `this`, but you can't do that. Instead, I'd need to pull the value out of the loop so that it can keep its value and increase throughout the loop. --- **4) *data\[i\]* vs *data\[i - 1\]*** Although I ended up going a different direction, based on the above, wherein I pulled the total out of the loop altogether, I did explore my original approach, which wrongly used `this.total` inside the loop. If I had wanted to truly use `total` inside the loop, I'd need to access it as a subset of `data`. So instead of writing `this.total`, I wrote `data[i].total`. (if you have a keen eye, you can see I dropped the "$" from my `i` variable) Turns out that `data[i].total` still doesn't work: ``` Error in render: "TypeError: Cannot read property 'total' of undefined" ``` It doesn't work because I'm still in the process of defining that index. So as the code looped and did `data[2]`, because it hasn't been defined, trying to access the `.total` of an undefined index, is undefinied. Using `data[i-1]` solves this problem. So for example, when `years_to_go == 2`, you want to base the `total` off of `years_to_go == 1`. As such, you need the `-1` in the data, or else you're trying to access a value (the total), that does not yet exist in the loop. --- As a small side note, I ended up not using vue-chartjs, the Chart.js wrapper for Vue.js. Although I found a lot of references in much of the material I found, it was pretty clear to me that using it would be a bit overkill, especially for a single chart. For implementations that utilize multiple charts, with varying sets of data and styling, this wrapper would tremendously lift that burden. In my case, it was unnecessary. --- To wrap up this project, I'll move forward with making the calculations a little more complex, and adding some styling so it can be a standalone page, rather than just a bare set of inputs. ### Empowering investors with Robinhood URL: https://www.bentumbleson.com/empowering-investors-with-robinhood/ Last updated: 2022-10-24T04:33:37.000Z ![](https://www.bentumbleson.com/content/images/2018/09/robinhood_glyph.jpg) ## Prologue I first signed up for Robinhood on March 17th, 2014\. At that point in time, the product hadn't even launched yet, and I was one of thousands clambering to try a product that promised *free* trading. I had dabbled with investing through my Dad back in high school, and up to that point in college, the only investing I had truly done was open an E\*Trade account. Robinhood represented something greater than zero-commission investing - it represented a whole new model for investing. My beta invention wouldn't be sent for more than 6 months, landing in my inbox on September 23rd, 2014\. That email specifically mentioned I had 72 hours to complete the application or my spot would be given to one of the 400K+ people still waiting in line. Much time later, I'm coming up on my four-year anniversary of joining Robinhood. To date, their maturity into a credible threat to the brokerage space has been nothing short of spectacular. A beautifully crafted product with a simple mission. Ideals that resonated with millions of Millennials and catapulted Robinhood to an almost [$6B valuation](https://techcrunch.com/2018/05/10/robinhood-rockets-to-a-5-6b-valuation-with-a-massive-new-funding-round/?ref=bentumbleson.com). There is still much room to grow, and as Robinhood eyes an IPO, they will continue to innovate to become a full-service financial firm. However, there is still one element that I believe Robinhood has a tremendous opportunity to capitalize on: **education**. --- ## Customer Validation Historically speaking, brokerage companies have truly only cared about their most wealthy investors. After all, with a business model that benefits from high net worth individuals and their propensity to place an abundance of trades (and collect those commissions), it's easy to understand why it typically isn't worth their time to land 100 new investors, with no more than a thousand dollars or so, versus the middle aged, tenured man with a portfolio already at six figures. This is a bit naive given there's a massive opportunity to address the needs of a generation that doesn't yet abundantly understand the value of the markets. Of course, there is more than visible from the surface, and doing a little customer research goes a long way in understanding the problem Robinhood is solving (making finance more available, particularly the markets). I think Robinhood is only scratching the surface as to *why,* and there still remains an opportunity to close the knowledge gap. Here's a study from Bankrate I recorded back in college: ![](https://www.bentumbleson.com/content/images/2018/09/Bankrate_1.png) You'll notice an overwhelming number of respondents indicated they simply don't have the money. This is reasonable - making 8% on $100 for a whole year would result in a negligible return - $8\. However, I would venture the benefit investing, even with small amounts such as this, is the belief that if you get people investing early, you're setting a good precedent for their future. **Think about it this way**: if you can rationalize putting aside 10% of your salary now, imagine how much easier it will be in the future to continue to contribute in greater amounts. This is an example of a core concept of money (the time value of money), that isn't as widely understood as it should be. I ended up conducting my own study in college with students to see if their responses differed: ![](https://www.bentumbleson.com/content/images/2018/09/not_investing_bt.png) Here's where the responses get a little more interesting. A small majority of *college students* indicated they weren't investing because they didn't have the money to do it. My argument above remains true, but Robinhood makes this much more approachable with the zero-dollar commission punchline. If you aren't paying $7/ trade, then you're not losing a few percent of your entire trade, every time you trade (I'm assuming first time investors trades are small in nature). **However when you take it a step further, you see that there's almost another 50% of respondents (across Bankrate's survey, and mine with students), wherein fund availability is not the reason they aren't investing.** > I wouldn't know how to get started/ don't know about stocks > I don't want to risk losing any money/ don't trust brokers > I don't want to spend the time to do it Would you not agree with me that these are serious concerns of potential investors that still largely haven't been addressed? Don't get me wrong, Robinhood makes it dead simple to get started investing, but there is almost no direction given on *how* to invest. Venture over to reddit's *r/wallstreetbets* and you'll find pages and pages of "investors" making increasingly reckless trades, typically on Robinhood. This isn't a slight to Robinhood, but I think it helps paint the narrative that first time investors could benefit from subtle financial education. --- ## One More Example I want to share one more example I recently pulled from my Facebook feed. I regret that I still check Facebook a few times a week, but sometimes I come across gems like this: ![](https://www.bentumbleson.com/content/images/2018/09/dogecoin_fb.png) Let me explain why this is an issue: 1. I love seeing people my age investing, but let's be clear that Dogecoin is not investing, it's gambling: > "It was a piss take, my whole point of dogecoin was taking a jab at all these alt-coins that were coming on the scene — like Initial Coin Offerings are today — and, you know, basically making a cash grab." - Jackson Palmer, creator of Dogecoin, January 2018. 2. This "investor" is speculating and attempting to time the market. [Burton Malkiel](https://en.wikipedia.org/wiki/Burton%5FMalkiel?ref=bentumbleson.com), best known for his book *A Random Walk Down Wallstreet* would call this a fool's errand: > “…and the lesson about timing is: not only do you not know when to get in, you don’t know when to get out. And when you market-time you got to be right twice. You got to know when to get out and when to get in. And nobody and I really believe this: nobody but nobody can do that.” (72:30) 3. This "investor" has already hit their trading limit, smartly imposed by Robinhood to prevent this individual from being classified as a pattern or day trader (note the "Day Trading Disabled" message at the bottom). To quote Robinhood's own support documents on the matter: > "Pattern day trading rules were put in place to protect individual retail investors from trading in a potentially risky way." With that in mind, the fact that this individual has already hit those caps tells me they are not equipped to be making rational investment decisions. Especially when you consider that the SEC states that you need at least $25,000 to not have these limits imposed. This individual has than than $1000. --- ## The Opportunity Robinhood is at the forefront of democratizing financial access, and there is a tremendous opportunity to utilize the brand recognition, trust they've built, and exceptional design talent to complement their own products (current and upcoming) by providing financial literacy and teaching core investment strategies and philosophies to a largely uneducated, but interested audience. These educational offering's will not only make investors collectively smarter, but more willing and rational to trust the markets across a range of investment vehicles. This will produce customers who are more likely to remain clients longer, grow their portfolios, utilize more services, reduce customer service requests, and ultimately better align their financial decisions to that of rational investors prepared for the long haul. ### Example: User story John is a recent college graduate, no more than a few years out of school. He has an engineering degree and a stable job which pays him a consistent salary. When he joined his company, he was told about their 401K program, including matching, but opted to skip it citing the need to pay down his students loans and not understanding much of the terminology involved. He has been hearing about cryptocurrency and often receives company-wide emails detailing his firm's financial status and holdings. He decided to sign up for Robinhood citing the recommendation of his friends and how easy it was to get started. He's put a few dollars in Bitcoin, and a few companies he liked such as Tesla and Boeing. He enjoys tracking his portfolio but doesn't enjoy the thought of so much fluctuation. The introduction of things like options trading is nifty, but largely do not make sense to John. He does not feel compelled to continually invest more funds into his portfolio, citing his general lack of understanding. "Investing" will just continue to be a hobby for him. **The opportunity:** John should have an IRA and most definitely a 401K through his company. It's great he took the initiative to start investing, but his broker hasn't given him any direction to better understand what he's doing, and allow him to continue to grow. Recognizing that John signed up and indicated he had no experience, Robinhood has the opportunity to introduce John to other stocks (Robinhood is in the process of doing this through their collections and social features), begin to explain the concept of a balanced portfolio (not overweighting any one stock or industry, etc), and introduce new concepts such as ETF's (collections of stocks traded as one). As it becomes available, Robinhood can introduce John to the idea of an IRA and help him build a sustainable portfolio for his future. --- ## How might we solve this? Forgoing a team activity of brainstorming around a whiteboard, a useful exercise at this stage is to propose a range of potential solutions, asking the question *how might we solve this?* > Create simple infographic pop-ups that can be accessed from the "notification panel" that teach relevant lessons in bite-size chunks > Create mini-lessons that appear in context throughout the application, such as when searching for stocks, viewing an individual security, viewing the overall status of your portfolio, etc. > Create other "tabs" on the main screen for viewing other statistics about your portfolio, such as sector weighting (with recommendations for diversification), and statistics regarding historical returns and risk > Add a tooltip/ designation to the top of the home screen that denotes the account as a "individual brokerage account" - this gives you the opportunity to explain on a new page different types of accounts, specifically brokerage vs retirement > Use "Collections" as a way to show sample portfolios, extending their capability from just a grouping of related stocks. For example, show recommended portfolios of gurus, suggesting similarity to a user's current one > Extend the Options trading "Discover" feature to stocks as well, to discover stocks based on themes such as big data or immunotherapy > Give users portfolios a "risk score" proportional to their age and the allocation of assets. Most users will naturally score high with their poorly diversified portfolios, but this actual direct feedback that may be of interest to a user > Give portfolio return statistics versus the overall market (SP500/ DJIX, etc)... allows a user to visualize on a YTD basis how they are tracking versus a benchmark (as this concept hasn't truly been introduced yet) --- ![](https://www.bentumbleson.com/content/images/2018/09/RH_sketches.png) ## Pen to Paper As I began taking my ideas and translating them into tangible concepts, I started sketching out Robinhood in my favorite design app. While drafting out some rough ideas, I came to understand that the common theme Robinhood could benefit from is a central "learning" center. All of the context-specific help is fantastic, but it all feels a bit wasted if there isn't a central repository to store and share these lessons. With a few ideas sketched out, I switched to Photoshop and started mocking a few simple ideas. While seemingly random in addition, I believe each addition above and as depicted can be elegantly woven together: ![](https://www.bentumbleson.com/content/images/2018/09/Robinhood_new1.png) If we take it a step further, you can see how simple ideas can begin to build on each other to provide a cohesive experience to users. ![](https://www.bentumbleson.com/content/images/2018/09/Robinhood_new2.png) Finally, let's tie together these simple quality-of-life improvements with the driving theme: an opportunity to provide simple, quality financial advice. Designed to make investors (particularly new ones) more informed and drive better decision making to help support their financial futures. ![](https://www.bentumbleson.com/content/images/2018/09/Robinhood_new3.png) This "learning center" has limitless topics that can be tailored to users, their portfolios, experience investing, and ongoing investment decisions. For beginners, lessons can include: - Understanding the time value of money and compounding - Different assets classes, and how they work to control portfolio risk - Portfolio diversification, and not putting all of your eggs in one basket - The fallacy of trying to time the markets, and why it isn't a sound strategy As a user matures, you can begin introducing more advanced concepts, such as: - Deep dive: What makes a company a good buy? Understanding earnings calls - Options basics and multi-leg strategies - Understanding Modern Portfolio Theory (MPT) --- ## Conclusion Robinhood has a unique audience that is poised to be responsive to modest educational opportunities that will not only allow them to become better informed about the decisions they're making, but allow Robinhood customers to grow and continue to utilize the products and services offered. As Robinhood continues to invest in its offerings, and its goal to become a full-service financial firm, they can realize continued corporate social responsibility by introducing educational opportunities to their clients. This will help reaffirm brand loyalty and help continue to solidify success in new products and services. --- Opening Photo Credit: [Robinhood (Dribble)](https://dribbble.com/Robinhood?ref=bentumbleson.com) Other graphics made by me. ### Approaching the problem URL: https://www.bentumbleson.com/approaching-the-problem/ Last updated: 2022-10-24T04:33:17.000Z ![](https://www.bentumbleson.com/content/images/2018/08/roadmap-dribbble.png) Like I explained in my introduction, [*Building from Scratch*](https://www.bentumbleson.com/building-from-scratch/), a challenge usually has more than meets the eye, and I think part of what makes a tech consultant, product manager, solution architect, etc. good at their job is understanding the depth of a challenge and translating that into a tangible solution. While I had grand ambitions of more complicated products, I ended up landing on the challenge of developing a savings calculator. It's not revolutionary, and definitely not something that hasn't been done before, but it's a good opportunity to build a simple tool with a socially conscious benefit, regardless of how basic. There are companies built entirely on the idea of providing calculators such as this one. SmartAsset comes to mind, or Wealthfront and their "Path" financial planning solution. Back to the problem at hand, how do I build a savings calculator? The end result is relatively straight forward: a user-configurable calculator that leverages a combination of assumptions and calculations to project savings amounts over time, presumably to estimate wealth pre and post retirement. Ideally the whole calculation can happen live with near-instant recalculation both in the underlying data and an accompanying chart. To accomplish this, I'll intend to follow this rough outline: 1. Have a web application perform a simple compounding calculation. This is exceptionally broad, but one of the more straightforward challenges. At the core of any programming idea is the concept of logic and variables. Build a formula, plug in values, and display the output. In the context of a time-varied element, there's more than one "output". Each year ends up being an output, but the base calculation doesn't change between each year. As opposed to Excel where you'd write the formula for each cell, this is likely accomplished with loops, storing the values in an array that can be fetched later. The most straightforward approach to accomplish this is to use Javascript. 2. Allow user-inputs (variables) to dynamically update underlying calculations. With my limited knowledge of JS, I can already forecast the difficulty of making the calculation dynamic based on user inputs. Luckily, my exposure to a few Javascript frameworks, namely VueJS tell me that utilizing one can lift this burden immediately. In the case of VueJS, [Declarative Rendering](https://vuejs.org/v2/guide/?ref=bentumbleson.com#Declarative-Rendering) is one of the first things the docs page explains. In my elementary understanding, this will allow me to dynamically link my user inputs to the underlying calculations to make them immediately reactive. 3. Include a time-series line chart, tracking the value over a period of time. I always used to wonder how sites like the NY Times used to come up with such complex graphs in their online articles. Luckily some digging years ago, and there's a range of fantastic Javascript libraries that help with charting. I've played with D3.js (which is used by the NYT in many of their dynamic data visualizations), but it has a steep learning curve for something simple in our case. I have experience using [Chart.js](http://www.chartjs.org/?ref=bentumbleson.com), a fantastic library for simple, yet powerful charts that provides out of the box support for things like animations, tooltips, scaling (think mobile vs desktop), and styling. The plan is to use [vue-chartjs](https://vue-chartjs.org/?ref=bentumbleson.com#/) as a Vue.js wrapper for Chart.js. Perhaps it's a tad overkill in my implementation, but if anything, the use of this will contribute to clean code and potential scalability in the future. 4. Live-update the chart with new data as it becomes available. This is something I don't currently have experience with, although the idea sounds relatively straightforward. As new data is available (as user inputs change), rebuild the chart to include this new data. Luckily Chart.js includes out of the box animations for events like this, a vue-chartjs even includes two mixins to support reactive data. As I've seen in other demos, I foresee this making the most sense to include a short pause between a user input and when the chart is recreated to prevent scenarios (such as if a user input uses a slider) wherein the chart is attempting to regenerate hundreds of times in a fraction of second. I'll save this tidbit for when I'm further along, but felt it was worth mentioning. 5. Increase the complexity of the formulas driving the data calculation. At the end of the day, if the only input my calculator supports is linear compounding, then I've really not made anything remotely useful. I'm not trying to reinvent the wheel, but there's a range of questions which can help produce a more accurate, or at least realistic calculation, given all of the factors that one would expect to influence savings over time. Once I have all of the other pieces in place, I can refine the calculation to better adapt to different scenarios. 6. Make it user friendly (UI/ UX), and refactor for efficiency. I've always been a fan of modern web design, and one of things I do well is designing elegant and functional pages. While the focus of this project is more learning and the underlying code, I would be remiss if I didn't give it a nice "coat of paint". Lastly, although the gameplan is to build everything in a scalable, adaptable fashion, I won't deny that the "first pass" of the code may not be the most efficient. Hopefully, by the end of this experiment, I'll have a base of knowledge for which I can build upon. --- Photo Credit: [Filip Greš](https://dribbble.com/GRES?ref=bentumbleson.com) ### Building from scratch URL: https://www.bentumbleson.com/building-from-scratch/ Last updated: 2022-10-24T04:32:40.000Z ![laravel](https://www.bentumbleson.com/content/images/2018/08/laravel.png) As a wannabe developer, my skills primarily lie in front end web design. Although I've managed much more technical developers, and regularly work with clients to transcribe requirements into high-level technical specifications. I'd say I know my way around the technologies I work with well enough to be productive. The one thing that I've never quite wrapped my head around was putting "pen to paper" or rather "hands to keyboard" to code up some of the more technical bits. I regularly `curl` API's, but I've never built an API, or automated the usage of one. As part of my work on [Carbon](http://trycarbon.co/?ref=bentumbleson.com), I've worked with programmers to help dynamically access API's to pull relevant financial data, manipulate it, store it, then return it to the user. In the simplest contexts, this is something like getting the stock price for an equity. Submit an equity symbol, get the price returned. Simple, right? In execution, it ends up being much more complicated than meets the eye. Just a few questions that come to mind: - How does your site know when to hit the API for that data? - Does a user manually have to submit the request via a form or input? - Do you automatically hit the API when a user lands on the page? - Do you hit the external API for every user, or can you pull a cached value to handle high volumes? - How you cache the value and refresh it? - How do you manipulate the API returned value to only return the attribute you're looking for? - How can you ensure you stay within the usage limits for the API? Some of the answers to these questions are process-related, others require technical solution for which I do not have the answers to, despite how simple they may be to an even intermediate developer. --- I've been trying, though half heartedly, to learn actual "programming" for awhile now. All I really know is markup languages, a bit of SQL, and a fundamental awareness of JavaScript. But I've never truly learned any object-orientated language. I remember way back in 2008 downloading Xcode on my Hackintosh Dell Mini 10v. In hindsight, no one could realistically develop on that 10" machine, but I thought I'd be on the forefront of the "app store goldmine". These 10 years later and that dream never really panned out. Opposite of that, I've been exposed to a lot of PHP from side projects from good friends of mine. I'm also regularly exposed to Java at work. Personally, I've worked with developers that utilize Laravel (PHP framework), and recently also got pulled into Vue.js. I follow quite a few successful developers on twitter, and I enjoy getting glimpses into what they're working on with the cutting-edge of tools and frameworks they're using. In many ways, I envy their skills. I envy the fact that they can bring an idea from concept to execution without needing external resources. ## How to learn? There's a tremendous wealth of fantastic resources all over the internet on how to learn all sorts of languages and libraries, methods, and techniques. Anything you want to know, you can learn on YouTube, uDemy, Team Treehouse, CodeCamp, just to name a few. I've become particularly fond of Jeffrey Way's [Laracasts](https://laracasts.com/?ref=bentumbleson.com) lately, which are "bite sized" videos in a range of topics, though they all relate to PHP and Laravel in some capacity. I've watched a lot of videos. As I spend a lot of time on flights these days, I usually have 6+ hours a week of wholly uninterrupted time in the air. While much of that time ends up being a nap, or a movie, I've challenged myself lately to watch more "educational" content, including introductions to PHP, Laravel, Object Orientated Programming, Vue.Js, and Chart.js. Yet at the end of the day, I still find myself struggling when I'm staring at a blank file. In many ways, it's an issue of not knowing where to start. Unlike markup languages that usually live together, languages such as PHP rely on functions, classes, objects, etc. that live across the structure of a project. Combine "*where do I start?*" with an underdeveloped intuition for object orientated programming, and I find it tough to execute on any idea that pops into mind. I tried to remember back how I learned HTML and CSS, and it was primarily from experience. I remember modifying the CSS on my first Wordpress blog, using bits of HTML to format my text - something I've never done with PHP. Although I have access to a number of custom, unique PHP-based repos, I've never spent the time to sit down and understand what I'm looking at. **So I'm going to challenge myself.** Execute on my next quick idea from start to finish. Idea through execution. I would love to work on Carbon directly, but it's current codebase is too far above my own knowledge that I fear I'd spend too long trying to learn something way above my level. Even learning it, my contributions would likely not mesh well with what we've already built. I think starting from scratch will be good learning opportunity. I've got something in mind, but want to formalize my thought process a little more before I lay the groundwork for where I'm going. Stay tuned. --- *Photo Credit: [Laravel Homepage](https://laravel.com/?ref=bentumbleson.com)* ### Conversations, not broadcasts URL: https://www.bentumbleson.com/conversation-not-broadcast/ Last updated: 2022-10-24T04:31:03.000Z ![Back in the Day](https://www.bentumbleson.com/content/images/2018/07/undraw_back_in_the_day_knsh.svg) ## The Internet of the 2000's The Internet is a very different place than it was a decade or two ago. I don't have much room to talk, but I have early memories of AIM (Adium) always being on, using StumbleUpon, Digg, and occasionally del.icio.us. I remember joining forums to learn more about hacking Xbox's, or the launch of the Microsoft Zune, or how to convert my Dell Mini 10v into a Hackintosh. Everyone was happy being a contributor, and you become known on the items you were most knowledgeable about. People didn't seek validation, there was just a sense of community that the internet had only recently enabled. Internet safety wasn't as much of a concern as it is today either. You didn't wonder if the anonymous individual on the other end of a forum post was a creeper, hacker, bot, or "government-sponsored" manipulator. You didn't even wonder if the content you were reading was accurate. I felt like the internet of the 2000's people had a more healthy sense of skepticism in what they were reading. Fake news and alternative facts didn't exist, and even if they did, there weren't mediums that actively supported individuals in broadcasting inaccurate information. Don't get me wrong, wire fraud and Western Union email scams have existed practically since the day the internet came online, but there was this inherent barrier to getting online that prevented those most likely to fall victim to them from participating. ## Forums for everyone Some of my fondest memories of the internet was my participation in a handful of forums. Although I had never met a single person on any of these sites, everyone bonded over a mutual interest in the same topics. And in hindsight, the best part about all of it, is that the internet of the 2000's fostered **conversations**, unlike today in the era of social media, where we all participate in **broadcasts** to our audience. More on that a bit later. One of the biggest modern day examples of a forum is Reddit. Each subreddit is effectively a standalone community, with very minimal oversight. Reddit seemingly solved the problem of having to find a new community for each thing you were interested in by putting it all under one roof. Quite an accomplishment, and as someone who has tried to start their own forum (circa 2009), I can't imagine how unbelievably difficult it was to start producing content. Yet Reddit really didn't start out being a place to *produce* content, it simply curated it. Well, its users curated it through upvotes. This wasn't totally new to what Digg did, reddit just did it better. Digg also made some questionable decisions about their business that drove many away. But I digress. The biggest difference in my mind that separated forums from Reddit was that forum posts were largely never "voted" on. A posts "rank" did not influence how visible it was to the rest of the forum audience. In fact, the popular posts were the one's that spawned conversations (replies), and not necessarily what people found interesting. In many ways, this was a good thing because it prevented the "hivemind" mentality you often see on Reddit. ## Broadcasts crave validation In today's era of social media, we all seek validation of what we're doing. We seek likes, thumbs up, +1's, upvotes, karma, you name it. There's a feeling of accomplishment with a successful "broadcast" across any medium these days. As an effect of this mentality, social media doesn't fit the mold of having a conversation online. After all, you're only sharing what you want people to know or think about you - in many cases, this is only the best parts of your life. ## Influencers on social media The best of us aren't necessarily the biggest contributors or the wisest. Instead, they're "influencers" who game algorithms, participate in clickbait, and otherwise draw attention to themselves. Now there are genuine influencers on the internet these days, they are just fewer and farther between - despite being easier to find. In many ways, the internet of today has enabled this bias that there's an intent of harm when you're online. Just like we all have our preferred news channels, we all consciously watch and filter for what we don't want to hear - cognitive dissonance. And there are many tools that make this easier than ever. On the topic of sharing content, this is not to say that folks in the 2000's shared *everything* online, but because everyone's sphere of influence or network was smaller, more genuine, and organic, there largely were no repercussions of sharing more intimate details of your life. Compliment that with a virtual identity (think usernames versus real names), and there was a great sense of community that I think is mostly lost today. ## Transitioning to big data When I talk about the internet today, I think it's worth noting that there are tremendously more people on the internet then there was 10, 20 years ago. The "tools" of today didn't exist, so while I might be nostalgic about what used to be, it's more likely that we're now the minority of the internet, rather than the majority. And not only are we a minority, but the content of the last decade is being monumentally dwarfed but what's is collectively produced today. The mass enablement of internet-enabled devices is a revolution that has allowed us all collectively to produce tremendously more content than ever before. IBM estimated that each day we produce 2.5 quintillion bytes of data, as of early 2018.[\[1\]](#fn1) To put a little context to that, that's 2.5 million terabytes each day, or 2.5 billion gigabytes. Let's take an example to visualize that: Consider that Apple's latest gen of iPhone's start at 64GB of storage. That means each day, we collectively produce enough unique content to fill 39 million iPhones each day. *Let's not forgot that an overwhelming majority of content produced ends up in huge data centers run by some of largest companies of our generation. It's strictly a visualization to think about data being produced in the context of iPhone storage.* In many ways, this is fantastic. Almost anything you want to know you can find online, but it can sometimes be difficult to maintain a sense of community when you're one of a million on the same channels. ## Conclusion The internet of today is a wonderful place which brings a lot of brilliant people together every day to collaborate on some of the most groundbreaking topics of our generation. While I do remain nostalgic of simpler today's, sometimes wishing I was just a few years old to have experienced it more, I don't have reservations about what the internet has become today, and I look forward to what the next 20 years will bring. Maybe it'll all come back to the beginning when we collectively realize social media isn't as great of a thing as it seems. As Bob Dylan would say, *the times they are a changin'*. --- 1. [https://www.domo.com/learn/data-never-sleeps-5](https://www.domo.com/learn/data-never-sleeps-5?ref=bentumbleson.com) [??](#fnref1) ### A better way to board an airplane URL: https://www.bentumbleson.com/a-better-way-to-board-an-airplane/ Last updated: 2022-10-24T04:28:08.000Z ![airplane](https://www.bentumbleson.com/content/images/2018/06/airplane.png) I fly a lot. Maybe not as much as some; I'm no "million miler", but I did fly approximately 100K miles last year, and have crossed 50K miles this year. In the world of *things that cause unnecessary stress*, boarding a plane is one of them. Everyone boards a little differently, and they all suffer from the same issues that leave the boarding process feeling inefficient and ripe for restructuring. --- *To understand the stage for boarding a plane, you need to understand where we're at now:* ## *How many boarding groups is too many? Where each airline falls flat.* American has 10 boarding groups, Delta has 7, United has 6, and Southwest (for all intents and purposes) has 5\. What I've found flying each dozens of times, is that most of them usually have "stacked" groups, wherein a disproportionate amount of passengers board in a single group, or too few in others. A great example of this is Delta's Sky Priority group, which encompasses basically any semi-regular travel. On peak business flights (Monday mornings/ Thursday evenings), I'd venture that this Sky group is approximately a third of the plane. **American**'s new set of boarding groups creates a scenario where almost no one (occasionally a person of two) boards in Group 2, 6, or 8\. As a gate agent, it can be a pain to call out so many boarding groups, and I've found that gate agents regularly group together 2, 3, and 4\. As well as 6, 7, and 8\. What this produces is a scenario which negates much of the status-earned group you're originally assigned to. What's the benefit of being in Group 2, if you're going to line up a clump of 2, 3, and 4 folks? American's system is far from ideal, but at least it's logical (e.g. starts with 1). **Delta**, on the other hand, I've found is confusing for first time passengers. If your boarding pass says "Zone 1", the uninformed might assume that they are boarding first! Since Zone 1 is typically used for Comfort Plus seats, uninformed travelers regularly assume that the $30 extra they paid for their seat with 2" of extra leg room also affords them boarding first, whereas in reality, Zone 1 is actually the fourth group to board (behind pre-boarding, premium, and sky zones). This produces a situation where passengers regularly crowd the gate too early. "Sky" is usually overcrowded, like the Group 4 of American. **United** isn't the best either. With a similar passenger stacking, Group 2 regularly includes up to a quarter of the whole plane. With that in mind, the very last person to Board in Zone 2 is just the same as first person to board in Zone 3\. Although in truth, there's not much you can do about this. The one thing United does right, however, is they are sticklers for making sure you board in your group. Too often I've seen gate agents (of other airlines) shrug when passengers board out of order with no consideration to the rules that govern the rest of the passengers. **Southwest** is one of the more interesting airlines. I'd noted they have 5 boarding groups (A1-15, A16-30, Families with Children, B1-60, C1-60). With the exception of the family section, everyone boards in order, so there is a much smaller range of benefits that is diminished relatively evenly with passengers (e.g. early passengers take the overhead bins and window/ aisle seats). Southwest's model is a "love it or hate it" thing. I float more into the latter camp, but that's a story for another day. Since you don't actually pick a seat until you board, there is significantly more incentive to want to board early (Southwest capitalizes on this with its "Early Bird" check-in), although once onboard, the only differentiation is largely just aisle, middle, or window. With the exception of the bulkheads, and two of the exit row seats, the seats are the same. **Net net** \- if you have too many boarding groups, you risk unintended consolidation and frustration from passengers (especially those in the later boarding groups, feeling like they're getting the short end of the stick). Whereas if you have too few groups, you risk negating the benefits provided to each group since you've minimized the differentiation between passengers. Not to mention in all of these scenarios, we haven't addressed how your seat choice influences your boarding order. In recent years, United experimented with loading window passengers first, then middle seat, then aisles. In theory, it's a good idea since if everyone boarded this way (throwing out any pre-boarding, early boarding privileges), since you would never have to wait for someone to get out of their seat to slide in. Unfortunately in execution, it didn't succeed quite as planned, and united scraped it shortly after. --- *With that covered, let's consider the next challenge. What drives our behavior to board in a certain order?* ## *Do you want to board first or last?* We're going to throw Southwest's "pick your seat" boarding out for the time being and assume that everyone has an assigned seat before they board. Based on that, in an ideal world, do you want to board first or last? **Ideally last.** Think about why people want to board a plane first: overhead bin space. Since your seat is reserved, but overhead bin space is not, the primary incentive of boarding early is to ensure you're able to snag a spot for your bag. Especially one that is near you and not many rows back. If you didn't have to worry about bin space, the reasons to board early do not carry much weight: getting a cocktail/ drink early (only applies to first class), not having to wait in the jet bridge, sit down early, etc. Almost none of these as benefits, outweigh the inconvenience of having to check your bag after you've already boarded. ### If bin space was reserved, the primary incentive to board early is materially minimized. This is almost to the point that I'd venture you would create a more even distribution of when people want to board. The days of overcrowding the gate would disappear since there would be little incentive to board early since you'd already have that guarantee. --- *Let's check the numbers:* ## *How many passengers can bring bags on board?* The most common plane is the US is the Boeing 737\. Its 700 and 800 configurations make up about 18% of all commercial planes in the US[\[1\]](#fn1). As such, these planes were used as reference for some of these calculations. | Airline | Plane | Seats | Bag Space | Utilization | | --------- | ------- | ------------------- | ---------- | ------------ | | American | 737-800 | 160 (16 + 30 + 114) | \~100 bags | 63% | | Delta | 737-700 | 124 (12 + 18 + 94) | \~70 bags | 57% | | Delta | 737-800 | 160 (16 + 36 + 108) | \~100 bags | 63% | | United | 737-700 | 126[\[2\]](#fn2) | \~70 bags | 56% | | United | 737-800 | 166[\[3\]](#fn3) | \~100 bags | 60% | | Southwest | 737-700 | 143[\[4\]](#fn4) | \~70 bags | 49% | | Southwest | 737-800 | 175[\[5\]](#fn5) | \~100 bags | 57% | | | | | | **Avg: 58%** | - *Note that *Utilization* refers to the percentage of passengers (assuming a full flight) that will be able to utilize the overhead bins for their bags. The higher the percentage, the higher likelihood a passenger has of being able to find a space for their bag in the overhead bin space.* - *Note that *Bag Space* refers to an average of how many bags that aircraft can hold: 737-700 planes have 20 bins \* 3.5 bags/ bin = 70 bags and 737-800 planes have 28 bins \* 3.5 bags/ bin = \~100 bags. Each of these calculations already does not include 1 whole bin, which is typically reserved for safety equipment, or crew bags.* ### Key Takeaway: A little more than half of the passengers boarding a plane can put their bag in the overheard bin. More realistically, this number is actually lower across the board, since there is usually an issue with passengers: - Bringing abnormally large bags that require them to be put in the bin horizontally (and thus taking the space of \~3 bags) - Putting their backpack/ briefcase in the overhead bin instead of under the seat in front of them - Placing their large coats in the bins (particularly during the Winter months). Despite flight attendants reminding folks of these items, there is usually minimal enforcement, and sometimes passengers even engage in these activities maliciously (or strictly for their own benefit). *Sidenote: I once asked a passenger to move his backpack under the seat in front of him, wherein he refused, citing his height and that he needed the extra leg room. Needless to say, his "6' 2" frame" is the same as me, and I've never had an issue fitting in my seat.* Since it's about a 50/50 shot of getting overhead bin space for your bag, and with 3/4's of the US's major carriers charging for checked bags, it makes sense that an overwhelming majority of flights in the US include a battle for bag space. Throw in the convenience of not having to wait for your luggage, as well as wholly negating any possibility of a lost or damaged bag, it's not hard to understand why most folks prefer to carry on their bags. That "battle" produces a heightened incentive to want to board first, which causes congestion at the gate, and increases the time to complete boarding while a good chunk of passengers will need to fight for bin space. This regularly includes passengers having to place their bag many rows behind them, then fight the current back upstream to get to their seat. I'd argue **this part of the boarding process is one of the most inefficient and contributes sizable amount of avoidable delays due to boarding**. --- *Since finding bin space is a material contributor to boarding slowdowns, lets correct the bag issue.* ## *How much is overhead bin space worth?* We've started to see airlines roll out their "basic" ticket packages that only allow you to bring a bag that'll fit under the seat in front of you. In American's case, the roll-out of that tier didn't actually reduce prices, but raised most other fare classes to compensate for the deficit that the "basic" ticket provides. Nonetheless, American and United charge approximately $40 - $50 for a roundtrip ticket to "upgrade" out of the that basic class. Here's what you get for those extra funds: ![Basic Economy vs Economy](https://www.bentumbleson.com/content/images/2018/06/Screen-Shot-2018-06-17-at-11.37.55-PM-1.png) *Photo Credit: Google Flights* - Access to overhead bin space - An ability to pick your seat prior to boarding (within reason) - An ability to change tickets (fly standby, etc) - Eligible for upgrades (with proper status) The last three items on this list are what I could consider "givens", or standard options provided by airlines. Since all four of the major US carriers are not classified as budget carriers, they are simply not setup logistically (or otherwise) to operate the same way budget carriers do. In that they do have not have near the flexibility to "piece out" components of the flight like budget carriers can do. With that in mind, lets approximate that 50% of the ticket hike can be attributed to being provided (or rather the right, but not the guarantee) the access to an overhead bin (*a generous overstatement of the value of the other "benefits"*). Therefore we might say that **you're effectively paying (or receiving the benefit of) $10-$12 to place your bag in an overhead bin each way**[\[6\]](#fn6). Given that checking a bag altogether runs about a minimum of $50, this is seemingly reasonable. Especially given you're making an airlines job easier (no additional screening, loading, and unloading; not to mention the write off's that lost or damaged bag claims create each day). So if you start to associate a cost with this benefit, you'll have a supply and will need to balance it with demand. Since supply will always be fixed, you'd need to control demand to most accurately match. As today there's effectively uncapped demand, almost every single flight deals with this issue of "first come, first serve", which again produces unneeded stress as passengers wrestler for their place in the boarding line. --- *Since finding bin space is a material contributor to boarding slowdowns, lets correct the bag issue.* ![Overhead Bin Space](https://www.bentumbleson.com/content/images/2018/06/Screen-Shot-2018-06-17-at-11.33.16-PM.png) ## *Can you reserve bin space?* This is one of the more interesting questions that I don't think airlines have truly considered. The model in the industry is almost ubiquitously uncontrolled space, so putting the process in place to *reserve* these spaces would require some process changes, potentially cosmetic/ hardware, education to the consumer, and deeper integration with ticketing and frequent flier accounts. If you've been on plane recently, you might have noticed that the "economy plus" section typically has signage that indicate that it's reserved for those customers. While I've never seen this actually enforced, this is a rather mundane and undeveloped approach to handling bin space. There are still no guarantees that you as an "Main Cabin Extra" (or what have you), will have bin space. Though it's unlikely you'll have an issue if you board early enough. Another thought might be to install dividers or fins into the bin space. While retrofitting an entire fleet would be costly, you'd provide a very easy way to make sure a passenger only uses their space. With a relevant identifier (e.g. Bin 2C), you've provided an avenue to allow space to individually reserved. However the better answer seems to be, why not simply refer to the whole bin as a spot? Since we know how many bags should fit, you would simply provide each passenger with an assigned bin number to use. Of course, for this to succeed, you need two primary items: - Good enforcement. Like reserved parking spaces, you'd need to make sure passengers don't abuse the right to a bin and choose a bin that isn't there's. Especially since airlines won't be able to always match a bin space with a passenger, there may be cases where a self-centered passenger may be inclined to take a bin that isn't there's - even if they've been assigned one. - There needs to be more consistent and stronger adherence to approved bag sizes. Since this approach relies on almost precise bag sizes and aircraft bin sizes, allowing passengers to bring oversized bags would disrupt the order and create a chain-event of issues that would threaten to materially lengthen the boarding process. But there is one last piece of the puzzle. --- *With a limited supply, how do we balance demand?* ## *Can you incentive passengers to not use the overhead bin space?* We spoke earlier about how the value of access to the overhead bin space is approximately $10\. While I'm sure airlines would love to slap an extra $10 on everyone's ticket to use the overhead bin, you'd likely create an even bigger problem as gate-checking a bag would likely become all too common, which would increase the demand on ground crews, without the typical cost benefits that come from checking a bag prior to security. It could be easy to simply tack on a fee for gate-checked bags like so many budget airlines do, but this is likely to frustrate the average customer. With that being said, I think flight prices are dynamic enough that a long term approach could entail including the right to a bin, directly on to a ticket. Afterall, every ticket in the US has included a $5.60 fee to support airport security. Even when booking flights with miles, you still have to pay that fee. I'd argue it's largely underutilized (or rather misappropriated) funds, but that is neither here nor there. More near term, I think airlines could approach the problem differently. - For all existing status tiers and first class members, include guaranteed space to a bin. If they don't need it, offer them miles to skip bin access. After all, status and first class members already can check bags free. If you provide them more incentive to *not* bring their bag on the plane, they might very well take you up on it. Since each flight effectively generates thousands of miles credited to frequent flier members, I have to imagine the cost of handling out additional miles in small chunks would be almost negligible in the perceived cost to the airline. - The beauty here is that you don't have to earn miles at the same rate by which you spend them. In that I mean an average flight might earn you (with no status), 1000 miles. Based on a valuation of say 1.5 cents/ miles, you've just earned $15 worth of rewards. We spoke earlier that the value of a bin is likely *worth* $10, but since you'd want to keep earning proportional to that of the flight itself, you could provide passengers an extra 100 or 200 miles to skip overhead bin space and it'd likely be well received, and that's an easy 10% - 15% boost in earning! Not to mention, those 100 miles will only cost the airline a few dollars, max. Just like with hotel loyalty programs, you can usually receive a sizeable credit for skipping housekeeping for a night. The same premise applies here. It's a win-win really. But what about for passengers who aren't frequent fliers or don't have interest in miles? - Provide credit for a free cocktail or snack - Lots of airlines do this as gestures for a variety of reasons. For example, Delta regularly adds coupons to frequent flier accounts, wherein Southwest regularly mails stacks of four to passengers (no status included), likely as a way to encourage their next flight. The cost of a cocktail or snack-box is likely very low to the airline itself, but the perceived benefit is much greater. Offering passengers a simple coupon for a drink or snack in exchange not using overhead bin space is a simple, cost-effective way (with a high perceived value) to lessen demand. - Give them a discount or wave the fee associated with checking a bag altogether - Since passengers already have an expectation that they can bring a bag onboard, there are many angles airlines could take to subsidize the value of a bag to be checked. For example, if airlines started asking you to disclose your carry-on baggage during check-in (and let's reasonably assume that passengers are smart enough to know the difference between checked luggage and carry-on luggage in this case primarily being size), an airline could forecast demand well before passengers board. Consider that over time, airlines could produce models that forecast exactly how much demand they expect (in terms of demand for overhead bin space) for each flight. - Just as airlines have complex predictive models for forecasting demand for the *seats* on a plane (in order to maximize revenue from cancellations, etc), the data from this model would allow an airline to very reasonably minimize the amount of incentives they need to provide to passengers. For example, if a well-trained model estimated that only 50% of the passengers on a plane were likely to carry a bag onboard, there is little reason for an airline to hand out incentives (such as miles, drink coupons, or bag check discounts) if they don’t need to. Whereas in the model returned something like 90%, the airline knows it would need to aggressively work to correct demand through incentives such as the above. As a related note, there is **huge opportunity** to train customers and set expectations regarding the likelihood of being able to bring a bag. What I’ve regularly seen flying is most gate agents will instinctively recommend to passengers in the later boarding groups that they may be required to check their bag as the likelihood of overhead bin space being available is near zero by the time they can expect to board. If airlines were to extend this knowledge to the check-in process, the “upsell” of recommending passengers check their bag is much easier. For example, if a passengers has to go to kiosk in the airport to get their boarding pass (they haven’t checked in online or via their phone), then depending on their boarding group, how many passengers are expected on the flight, and an estimation of how many bags can be accommodated, and airline could make an immediate upsell to have passengers that might not have otherwise checked their bags, check them. To help illustrate this example, I've put together a high level process flow that explains what the check-in process at Delta airport kiosk looks like. Note that this process is almost identical to passengers who complete check-in on their mobile phones, with the exception being that the boarding pass is digitially loaded on the phone. [![Delta Check-In Process - Original](https://www.bentumbleson.com/content/images/2018/06/delta_boarding_process_original_bg.png)](https://www.bentumbleson.com/content/images/2018/06/delta%5Fboarding%5Fprocess%5Foriginal%5Fbg.png) **Click image for full size. Figure:* A simplified process flow of the Delta check-in process available via the kiosks at airports throughout the US.* #### *Example: A family of four* Take an not all to unrealistic scenario of a family of four traveling for an annual holiday. The cost-conscious parents instruct the whole family they are only to have carry on bags to avoid having to check bags (and add an extra $200 in costs each way). In this example, this family still might be in one of the last boarding groups on a full flight (say to a popular beach destination). While checking in the family, the airline can offer the whole family the convenience of checking all of their bags free. In this example, the check-in process could make a reasonable assumption that because the family is already planning on bringing the bags on board, paired with the full flight, and a high likelihood (given their boarding group), that they will be forced to gate check all their bags. And as such, the check in kiosk can present this as a warning, with the recommended mitigation of checking all of their bags now, free of charge. [![Delta Check-In Process - Upsell](https://www.bentumbleson.com/content/images/2018/06/delta_boarding_process_upsell_bg.png)](https://www.bentumbleson.com/content/images/2018/06/delta%5Fboarding%5Fprocess%5Fupsell%5Fbg.png) **Click image for full size. Figure:* Seen above, once an airline determines how many bags a passenger intends to bring on the plane, they have an opportunity to upsell them with an incentive to check their bags. It's worth noting this "upsell" is very similar to airlines that discount the "upgrade" to first class or premium economy during check-in as a way to maximize revenue for the flight.* In our example, an airline can note that this is *not* lost revenue, since this family had no intention of checking their bags anyway. And by checking the bags now, you’ve eased the logistical challenges of gate checking a bag, increased the convenience of the family, while leaving them satisfied with a presumed savings of over $200! [![Delta Kiosk Upsell](https://www.bentumbleson.com/content/images/2018/06/delta_kiosk_upsell_vF2.png)](https://www.bentumbleson.com/content/images/2018/06/delta%5Fkiosk%5Fupsell%5FvF2.png) **Click image for full size. Figure:* A mockup of the "upsell" page a passenger might expect if they indicated they intended to bring their bags aboard the aircraft. In this case, the passenger(s) can recognize an immediate benefit in the form of waived bag fees.* --- ## *Conclusion* Boarding an airplane is consistently a difficult process regardless of what airline you're on. While airlines have worked to manipulate the way in which customers get their seats in an effort to speed up boarding time, little emphasis has been placed on how carry-on baggage affects the boarding process. I would venture that as airlines continue to experiment with overhead bin space and access, they'll find that this is directly related to the desire to board a plane first, which in term affects the speed and efficiency in which an aircraft can be fully boarded. In a basic implementation, I propose that airlines began assigning overhead bin space just as they do seats. With enough data, the likelihood of having to have passengers gate check their bags, board with a bag for which there is no space, or have to place their overhead bag many rows behind him upon boarding, will continue to shrink, leading to an expedited boarding process to the benefit of all. --- 1. [http://www.fi-aeroweb.com/US-Commercial-Aircraft-Fleet.html](http://www.fi-aeroweb.com/US-Commercial-Aircraft-Fleet.html?ref=bentumbleson.com) [↩︎](#fnref1) 2. United has three different versions of the 737-700 that seat 136, 124, and 118\. And average of 126 was used. [↩︎](#fnref2) 3. United has four different versions of the 737-800, wherein V1 seats 154, with V2, V3, and V4 all seating 166 passengers total. [↩︎](#fnref3) 4. Southwest flights do not have a separation of cabins - all seats are standard economy. [↩︎](#fnref4) 5. Southwest flights do not have a separation of cabins - all seats are standard economy. [↩︎](#fnref5) 6. Please don't quote me on this. It's strictly an approximation based on the relative price differences of different fare classes. [↩︎](#fnref6) ### How inefficient am I? URL: https://www.bentumbleson.com/how-inefficient-am-i/ Last updated: 2024-09-01T18:13:55.000Z ![employee-at-desk](https://www.bentumbleson.com/content/images/2018/06/employee-at-desk.jpg) I recently found myself wondering how you might go about measuring the efficiency of your contributions to a team. An odd thing to stumble upon, but it was grounded in a recent situation that I'd imagine isn't all to uncommon in industry: you and some coworkers are working on something together. A presentation, a report, what have you. You divvy up the work based on subject matter expertise and go heads down working for a period of time. Some time goes by and you do a check-in with your team and find that you are either significantly ahead of your coworkers, significantly behind, or anywhere on this spectrum. It's an interesting conundrum, as lets consider in this example that you've completed twice as much work as your colleague next to you (say pages in a report, lines of code, what have you). Would you say that you are *more efficient* or that your coworker is being *inefficient*? As all work is new work, there's really no baselines, outside of some loose guidelines for the expectation of how long this should take. *Sidenote: what is the best way to estimate time for tasks? Bottom up, top down?* At the end of the day, you've produced a scenario where you might not be able to call it day and leave your coworker behind, lest you start degrading your title of being dependable or reliable. If you're a more efficient worker, you're effectively penalized for it. As salaried employees, our pay doesn't adjust based on this productivity factor, whereas an employee working on commission might reasonably expect to see their financial benefit increase as they complete more work, make a sale, help a customer, etc. But put this is the context of your customer - If you're billing a client hourly, you're actually selling yourself short by being more efficient at the work you do. This is counterproductive as it rewards hours over results. And it makes the *less* efficient practitioners look like the better employee, especially if they have to stay late to meet deadlines. --- Photo Credit: Gregory Althoff | [Dribbble](http://https//dribbble.com/gregoryalthoff?ref=bentumbleson.com) ### India: Retrospective URL: https://www.bentumbleson.com/india-retrospective/ Last updated: 2022-10-24T04:25:33.000Z ![IMG_20180207_150639](https://www.bentumbleson.com/content/images/2018/05/IMG_20180207_150639.jpg) > *A view of the Deloitte office in Mumbai, which became my work space for the next 2 weeks.* Recently I was asked to travel to India as a part of my current engagement to meet the development team I’m currently managing. Outside of a study abroad to Israel my senior year of college, this was undoubtedly the farthest East I’d ever been. Traveling to India was going to be a unique opportunity to explore a culture wholly unlike what is in the United States and having recently returned, I wanted to reflect on my time there (Feb 2018). --- ![airport](https://www.bentumbleson.com/content/images/2018/03/airport.jpg) > Above: Scores of drivers holding signs awaiting their passengers from Chhatrapati Shivaji International Airport. Note this photo was taken at approximately 2AM local time. Flights regularly arrive and depart into the early hours of the morning. > > ## 1.3 Billion People > > Such an order of magnitude larger than the US, it’s almost tough to comprehend. With that many people, I think India was particularly slow to modernize or perhaps industrialize. You’d think with an endless supply of labor, you could get a lot accomplished, but it’s seemingly a double-edged sword. > > There is simply so many people around that labor is cheap, abundant, and largely unskilled. You can only have so many doctors, lawyers, taxi drivers, construction workers, etc. Despite a majority of my trip being in the "suburbs" of Powai, a typical commute felt like I was in a major city, with swaths of residents walking around with little regard for the traffic inches away from them. > > I talk about it a little the crumbling infrastructure later on, but a lack of sidewalks usually meant that those commuters on foot would regularly weave between traffic. Scary at first, but no one seemed phased at all. > > --- > > ![security](https://www.bentumbleson.com/content/images/2018/05/security.jpg) > >> *Above: A baggage scan, metal detector, and a patdown for males at a local mall in Mumbai. This is regular sight at busy hubs around the city.* > > ## Safety and Security > > Most places I traveled to in Mumbai had an excess of security. At first glance, it would seem reactionary to some of the horrific terror attacks in Mumbai from not too long ago[\[1\]](#fn1). Instead, it would seem that it’s easy to staff up security positions with minimally-trained forces and maximum results simply due to the size of the workforce. Would you rather have one Navy Seal protecting a building, or 100 standing all around it? > > On the topic of security, there are many individuals that have “high risk” jobs, which seems counter to the culture of that country (more on that later). I can only assume there is a better wage attached to the guys that man the checkpoints to search your vehicle before entering the premise of an office park, your hotel, what have you. Perhaps it’s just security theater? The model seems to work well in the states - the TSA is a largely unskilled labor force that has a pitiful track record of activity stopping terrorism. Instead you might argue that their presence alone is just as effective. > > As a sidenote - ever wonder why Walmart hires seniors and retirees to say hello when you walk in? Turns out, having that single individual there is actually a fantastic theft deterrent.[\[2\]](#fn2) > > My last thought on security, but in India, men must be scary. Almost any public place I went, I was required to walk through a metal detector and be frisked. The airport? Absolutely. The hotel? Yep. The mall? No question. My female co-worker got a different experience - since women are apparently low risk, they are subject to quite the level of scrutiny. Instead, they’re asked to step into a curtained box, where they are wanded with a metal detector for about 0.2 seconds max. > > --- > > ## Income Disparity > > ![IMG_20180201_180603](https://www.bentumbleson.com/content/images/2018/05/IMG_20180201_180603.jpg) > >> *A photo I took along my commute into the office - spotted amongst makeshift parking lot, a goat positions itself atop a stone.* > > As an extension of such a massive workforce with limited positions, there is a very noticeable wealth gap in all parts of India. In many cases, there are simply so many people that pop-up slums are all too common. It’s almost heartbreaking to see a modern slum setup right next to a luxury hotel. While commuting to work in the mornings and evenings, I felt like I got a glimpse of all walks of life. There are truly no “bubbles” in India. There are simply too many people to make it possible. > > This cycle of poverty causes the country to be exceptionally dirty. I found this odd because I felt like with so much unskilled labor, it’d be easy to keep it clean, but the folks that create the mess aren’t the ones that are incentivized to clean it up. Compounded over years in dense areas, the only areas that remain clean are the ones meticulously and purposefully maintained. > > ![IMG_20180131_171947](https://www.bentumbleson.com/content/images/2018/05/IMG_20180131_171947.jpg) > >> *A view of Raj Bhavan, one of the Southmost points of southern Mumbai, visible from across Back Bay near Nariman Point. The "haze" is pollution which washes out the view no more than 2 miles across.* > > In some ways, I think the lack of regulation contributes to this. The pollution is literally so bad that the forecast during my two week visit regularly included “smoke”. A distinction meaning the ability to determine the forecast was literally obstructed by pollution. I know sometimes in the US it’s easy to think that there’s not enough regulation (especially on hot button issues), but there is an immense amount of that keeps cities clean and running; silent regulation, if you will. > > The overall ambience of the country also suffers from aging infrastructure. Roads are poorly maintained and sidewalks and curbs are usually nonexistent, for starters. On multiple occasions, what I’d expect to be a sidewalk would be nothing more than a pile of loose rubble. I suppose with as much rain as they get during monsoon season, it can be tough to maintain, but in some ways it feels like the city simply doesn’t care. Even in a touristy area of Goa, I regularly had to dodge large rocks near the sidewalk. I’d imagine this is just asking for litigation in the US, but no one seemed to bat an eye at the amount of hazards most face on a normal day. > > --- > > ![IMG_20180205_210130](https://www.bentumbleson.com/content/images/2018/05/IMG_20180205_210130.jpg) > >> *A view from my hotel room window one night, where a lively wedding has happening in the courtyard below.* > > ## A Content Culture > > With so many people gunning for fewer jobs, India is very competitive. Getting a government position is coveted, as is a position in the military. In a competitive landscape, there will always be those that want to climb to the top, but at what cost? Through my interactions I found that many are very comfortable with providing for themselves and their families. Taking unnecessary risk, especially if you’ve already achieved so much (in such a competitive environment), would appear to be frowned upon. > > This is not to say there is not great innovation and entrepreneurship in India, but there’s a stark difference between India, and say Israel, which is seemingly entrepreneurial by necessity. But I digress - a hallmark of my trip in India was the value of the guest. Almost everywhere I went that had a service component to it, I couldn't help but feel like those everyone gleaned an immense amount of satisfication from their service to others. > > I can admire that regardless of the situation you're in, I never felt a sense of envy or jealous towards others or their surroundings. Everyone is largely comfortable, and there's something calming about that in a city (and country) that is always awake. > > --- > > ![infrastructure](https://www.bentumbleson.com/content/images/2018/03/infrastructure.jpg) > > A photo I snapped looking out from my taxi onto what is a common sight in India - aging roads supporting all-day rushhour traffic. > > ## Traffic > > And I thought the traffic in Chicago was bad. Seriously, it’s tough to talk about India without talking about the traffic. For starters, most roads don’t have lanes, and even the one’s that do, you aren’t really required to stay them or even use your blinker to change. When there’s no lanes, the flow of traffic is just an amorphous blob of cars, motorcycles, and auto-rikshawks (three-wheeled taxis). In India, everyone is an offensive driver, gunning ahead immediately and without hesitation. It would seem to me that the rule of thumb is that you’re only responsible for what’s in front of you, so any erratic weaving, acceleration, braking, or positioning isn’t your concern as a driver - you only need to be concerned about everyone else. > > In some ways, because there is little traffic regulation, everyone drives in a predictable manner. Horns are used not as a critical warning like we might use them in the US, but instead, it’s a form of positioning. As crazy as it sounds, I’d argue that drivers in India are experts with echo-location. With a horn constantly buzzing, you can get a pretty good mental picture of where other vehicles are relative to you. In many cases, I found our driver regularly blipping the horn to let other drivers in front of him know where he was. Even though every car on the road looks like it’s been in a fender bender, or ten, our driver never hit, or was hit by anyone or anything. I suppose you can be an expert in anything if you practice long enough. > > One last thought - because traffic requires such a high degree of focus, you cannot realistically do anything *but* drive. Texting while driving isn't even a concern, lest you take your eyes off the road for half a second and end up in a wreck. I'd imagine the split second your eyes are closed while sneezing it difficult while driving. > > --- > > ![food](https://www.bentumbleson.com/content/images/2018/04/food.jpg) > ^ *Above: While not the most colorful meal, this pile of lamb was tender and meant to be eaten with our hands. Delicious.* > > ## Indian Cuisine > > I won’t pretend that I have an exquisite pallet. In many ways, my diet is largely, if not exclusively, western. Having grown up in Kansas City, can you blame me when I’m surrounded by BBQ? On day one, I couldn’t get over how spicy my vegetable (peanut) salad was. My Mumbai-based coworkers gave me a hard time since I couldn’t eat anything without immediately reaching for my bottled water. I suppose I wasn’t conditioned like they were - but they have spices on everything. Rice, chicken, vegetables, you name it. Even “sweet” foods are more mild than I’m used to. Perhaps I eat too much sugar. > > One thing I will say, is that I appreciated the variety of dishes and flavors. I can’t say I enjoyed all of them, but there’s a healthy range of foods that you could stay within the Indian cuisine for awhile. Will I seek out Indian food back here in the States? Don’t plan on it - I enjoy my Western diet too much, but I’m glad I tried it. All about experiences, right? > > One thing I will note - I was very careful about what I ate so I suppose I didn’t get the true experience. I unfortunately had to pass on any foods from street vendors, only drank bottled water, and chose not to eat fruits and uncooked vegetables that have edible skins (strawberries, blueberries, etc). I’m sure my gut thanked me, if only just precautionary. > > --- > > ## Concluding Thoughts > > Flying about half way around the work is an experience. I left my house at about 10AM on a Saturday, and arrived at my hotel in India just past 1AM on Monday. In a way, I skipped the weekend, which made the start to my week that much more difficult (an 11.5 hour time change too). > > ![IMG_20180203_102246](https://www.bentumbleson.com/content/images/2018/05/IMG_20180203_102246.jpg) > >> *A view from my resort in Goa, where I spent the weekend between my two week in Mumbai.* > > It was an absolute pleasure getting to meet my team in their home office. Each are unique, creative, and talented individuals that help make a great team. Between bowling with them at a local mall, enjoying the cuisine, playing a few in table tennis, and getting to see my team in action - it was a fantastic experience that has brought us closer as a team, and helped me become a little more worldly. While it might be awhile before I return to India, I do feel as though I have so much more of the world left to explore. > > --- > > 1. [https://en.wikipedia.org/wiki/2008\_Mumbai\_attacks](https://en.wikipedia.org/wiki/2008%5FMumbai%5Fattacks?ref=bentumbleson.com) [↩︎](#fnref1) > 2. [https://www.wsj.com/articles/wal-mart-ushers-greeters-back-to-the-front-1434651944](https://www.wsj.com/articles/wal-mart-ushers-greeters-back-to-the-front-1434651944?ref=bentumbleson.com) [↩︎](#fnref2) ### Is productivity linear? URL: https://www.bentumbleson.com/is-productivity-linear/ Last updated: 2022-10-24T04:24:46.000Z ![productivity](https://www.bentumbleson.com/content/images/2018/03/productivity.png) When I started my undergraduate education, I began in a Master’s of Architecture program. And although I didn’t end up graduating with that degree, I’ve recently drawn parallels to some of my recent work, and the challenges I was assigned as a part of my classwork in that program. I’ve been told that you go to college to learn how to think; to put it more simply, if I were to start at point A, and I need to get to point B, what would I need to do to make that happen? Luckily A to B usually isn’t a big “jump”. Progress is clear, objectives are defined, and tasks materially contribute to completion. For example, any math class might teach you a series of “steps” you need to take to arrive at a solution. How about in an architectural studio? How does one simply “arrive” at a design? What questions do you need to ask that will influence your design? How much trial and error is necessary to move forward? In this case, sometimes it’s unclear how, if at all, the work you’re doing at a task level is contributing to a final product. One’s productivity might as well be zero, until you find the correct (or reasonable) route forward. If you were to graph the completion of a product/ solution/ feature/ etc over time, I would venture it is far from linear. Do the “flat lines” influence the slope of the future? Are flat lines unproductive? Can you bill your time for being unproductive? Is it productive to be unproductive? I’m reminded of an *The Office* episode wherein Michael hosts a “Movie Monday” in the office, effectively putting his employee's productivity to a temporary zero. When Jan, corporate leadership, appears, Michael insists that all of his employees will be more productive since they’ll now need to make up for the time they lost during their screening of “Varsity Blues”. Perhaps the accounting department and their rigid, predictable work may struggle to catch up, but how about others in more dynamic positions? …. In my free time, I enjoy hobby programming. I’m far from an expert, but I consider myself a mildly talented front-end designer. I find that when I’m designing pages, I can “spin my wheels” for days and days, sometimes weeks, trying to find a starting point for my work. It’s simply not realistic to just sit down and “code” without an idea of what you’re trying to design. Once I settle comfortably on a shell of a design, my productivity skyrockets - I can design the page and pivot as needed throughout the entire process. If I had been tasked by a client to design a page, with daily status reports, I fear 80% of those status reports would report no progress. Not because I wasn’t making any, but the repetitive cycle of initial “trial and error” does not consistently contribute to a final product. Isn’t it interesting for design services, there is typically a limitation of revisions? In these cases, each revision, each trial, is the equivalent of checking another box. In that, they’re making tangible progress, regardless of how lackluster each design might be. … Recently in my work, I’ve taken on the managerial responsibility of a deeply technical team. Our end goal is clear - implement a highly configured, and customized enterprise software package. Our timeline is huge - over a year. To relate it back to my point example, it feels like we’re trying to go from A to Z, with each milestone between ill-defined and lacking specificity. I find one of the more difficult tasks I deal with each week is developing achievable tasks that will help get to the next milestone. Some teams find success with exceptionally detailed project plans that can be tracked continuously, however, the prerequisite of this approach is a deep and complete knowledge of everything involved from start to finish. This is difficult in a setting where the work you’re doing isn’t necessarily repeatable. Other teams simply rely on the expertise of the team itself to take initiative at an individual level and recognize the next challenge they might tackle to contribute to the whole. You find this usually works well in an agile implementation where teams usually self-organize and build towards requirements that are clear. As a segway into my final point, it would seem that there's an increased emphasis on having a tangible and visible goal as it relates to work you or your team are trying to accomplish. How can you build a path forward if the end is unclear? I've heard this related to someone simply getting in their car and hitting the open road. It's irrelevant how fast you're going if you don't know *where* you're going, right? It would seem that the driver that took the time to plot a course, if even vague, would end up in the right direction and would mitigate a situation requiring them to backtrack. Maybe this is why goal setting is so critical? ### iMessage and the silent takeover of SMS URL: https://www.bentumbleson.com/iphones-and-blue-chat-bubbles/ Last updated: 2022-10-24T04:21:51.000Z ![imessage](https://www.bentumbleson.com/content/images/2018/03/imessage.png) If we think back to the beginning mobile messaging, the first thing that comes to mind is Blackberry and the Blackberry Messanger (BBM). In a world of SMS, the read recipts and primative smiley faces BBM afforded its users was unique. So unique infact, that RIM was convinced that this "feature" of their devices was so compelling, there was no reason to ever let it be outside of the "Blackberry ecosystem". At a time when iPhone's and other capable smart-devices came to rise, it seemed customers weren't swayed. Mind you disregarding RIM's other shortcomings, it would seem in hindsight that keeping BBM internal was a mistake. Evidence of this mistake? Back in 2010, a former intern of RIM went on to create Kik after having spent a summer working with the BBM team and wanted to create a "*BBM-like app that worked on all smartphones*" [\[1\]](#fn1) In fact, he even went as far as to offer to discontinue his work on Kik if Blackberry made BBM cross-platform. As history tells us, Blackberry said no. Fast-forward to today, and we live in a generation where communicating with a "green bubble" might as well be akin to carrying to a Jitterbug phone. Why are iPhone-owners so ambivilent towards their non-Apple-weilding friends? I won't speculate (I use a OnePlus 5 myself), but as I thought through this *situation*, there are some key decisions Apple conciously made: - You may recall prior to iMessage being introduced, *all* chat bubbles were green on iPhone. When iMessage hit the scene, Apple opted for a more pleasing blue color for those messages. Oh how easy it now becomes to relate the green color to old. - iMessage came to fruition in a period where "unlimited texting" wasn't near as ubiquitous as it is today. Positioning iPhone's as having "free and unlimited" texting was attractive to a market who used to pay for texts in blocks of 200. - Apple ecosystem has always been closed. Even after the App Store was released, you couldn't change the default SMS/ texting/ messaging app even if you wanted to. It was irrelevant if there were better apps - you simply couldn't use them. - Apple integrated iMessage (internet-based messaging) directly into SMS (cell-based messaging) in a mostly-seemless transition in which no one had to opt in to start using. What's the best way to push adoption? Don't make it a choice. - In a continuation of that point, I'd venture that many "casual" users don't know the difference between cell-based SMS, and data-based iMessage. iMessage might as well be a less powerful GroupMe, Hangouts, Whatsapp, etc. - And on top of all of this, Apple made it difficult to leave. So much so, they got sued for it.[\[2\]](#fn2) So why is it that these many years later, Apple's iMessage is a point of contention for most as why they want to stick with iPhone? **The short answer:** it's tough to leave an ecosystem. As someone in technology, I find that many carry iPhone's primarily for the "ease of use" that comes with communicating with friends and family via iMessage. Undoubtly by design, had Apple not limited the choices for third-party messaging apps, the "pre-installed" iMessage would be just another app in a sea of choice. Instead it's the defacto reason iPhone's continue to fly off the shelf. It seems to me that "*Think Differently*" doesn't really apply anymore. I'll leave with one thought - if you're in the Apple ecosystem, what's keeps you there? How many Apple services do you really use? Does the benefit of one (or a few) outweigh the shortcomings of the many? Not all companies have closed ecosystems, but Apple (and maybe Bose) are the gold standard for them. *Photo credit[\[3\]](#fn3)* --- 1. [https://www.thenational.ae/business/how-wounded-blackberry-and-bbm-failed-to-move-with-the-times-exclusive-book-extract-1.99108](https://www.thenational.ae/business/how-wounded-blackberry-and-bbm-failed-to-move-with-the-times-exclusive-book-extract-1.99108?ref=bentumbleson.com) [??](#fnref1) 2. [https://www.wired.com/2014/11/apple-lawsuit-imessages/](https://www.wired.com/2014/11/apple-lawsuit-imessages/?ref=bentumbleson.com) [??](#fnref2) 3. [https://dribbble.com/xerlee](https://dribbble.com/xerlee?ref=bentumbleson.com) [??](#fnref3)