How to create a (simple) watch app on Pebble

It’s been a couple of months since I joined Pebble as a full-stack web developer. Most of people know Pebble by its phenomenal success in its Kickstarter campaign – raising more than $10M. Pebble is also known as the company that started the smartwatch/wearable market.

Ever since I started, I have come to like a few watch apps and faces, and I’ve always wanted to create something on my own (since I like to build things). Even though we have a hack day every other week, I wasn’t able to participate because of things that needed to get done.  But today was a special one. It was hack night, instead of hack day, and I was able to participate.

In today’s hackathon, I built a simple app that displays a short quote like Unix fortune using Pebble’s pebblejs and CloudPebble. It was super easy. Almost too easy. I don’t interact with our dev evangelist team much, I have new respect for those guys. They worked hard to make it easy for developers to develop apps on Pebble.

I have shared my code on my github account.  You should check it out, and it should be self-explanatory. Use pebblejs as reference to look up functions.

https://github.com/yangtheman/fortunes_pebble_app

Here we go.

1. Enable Developer Mode on your mobile phone. On iPhone: Settings > Pebble > Developer Mode > On

IMG_3529

IMG_3527

2. Enable Developer Mode on Pebble mobile app. On iPhone: Pebble App > Settings (where you see My Pebble) > Developer > Enabled

IMG_3528

IMG_3530

3. Log in to CloudPebble using the same username and password as one you created when you first set up Pebble.

4. Create a new project and select Pebble.js as Project Type.

Screen Shot 2014-07-17 at 10.02.49 PM

5. Click on app.js on the left column.

Screen Shot 2014-07-17 at 9.32.22 PM

6. If you want, you can leave the default code as it is. Or you can copy and paste my code in pebble-js-app.js. Don’t publish your app using my code as it is, but you are certainly welcome to try it out and tweak it!

7. When you want to see the app run on the watch, click on the white Play button with green background on the right side of the screen. This should automatically compile and install the app on your watch. Make sure you have an empty slot in the locker on Pebble mobile app. If compile/install fails, toggle developer mode on Pebble mobile app as in step 2 above and try again. If you have any debug statements using console.log, you will see the log messages in ‘View Logs’ as you interact with the app.

Screen Shot 2014-07-17 at 9.32.12 PM

8. You can change your code and compile/run until you are satisfied. Next steps are for publishing the app on Pebble App Store.

9. Get some screenshots from your watch app. From CloudPebble, click on COMPILATION link. Then click on SCREENSHOT button. It will take screenshot of what’s on the watch. Take a couple of different screenshots.

Screen Shot 2014-07-17 at 9.41.50 PM

10. Get the PBW file. In COMPILATION view, you will see a list of builds. You probably want to grab the latest one (assuming the last build is the one you want to publish).

11. Log in to Pebble Dev Portal using the same username and password as one you created when you first set up Pebble, and click on Publish a Pebble App button.

10. In our case, we are adding a watch app, so click on Add a Watchapp link on the left.

Screen Shot 2014-07-17 at 10.09.01 PM

12. Filling out the form should be pretty straight forward.

12.1. For icons, make sure they are Black with White background. Do not use transparent background. 

12.2. Use the screenshots you downloaded earlier for screenshots.

12.3. You can be as creative as your want for the header image.

13. Once you are done, hit CREATE button at the bottom of the page.

14. You are not quite done yet. Lastly, go down to Releases section and publish your first version. 

Screen Shot 2014-07-17 at 10.11.01 PM

15. After that you can make your app public by clicking on the Make Public button on the top right corner of the page.

16. Ta-Da! Your app is ready on Android platform first. It will take a day or two to appear on iOS platform. There you go. You now successfully created and published the first app!

User-friendly 500 and 404 pages on Rails 3

This came in handy for me, so I wanted to share this particular way of handling 404 and 500 errors.

First, rescue errors and tell what to do in application_controller.rb.

  if Rails.env.production?
    unless Rails.application.config.consider_all_requests_local
      rescue_from Exception, with: :render_500
      rescue_from ActionController::RoutingError, with: :render_404
      rescue_from ActionController::UnknownController, with: :render_404
      rescue_from ActionController::UnknownAction, with: :render_404
      rescue_from ActiveRecord::RecordNotFound, with: :render_404
    end
  end

In the same application_controller.rb, then you create the methods specified above.

  def render_404(exception)
    @not_found_path = exception.message
    respond_to do |format|
      format.html { render template: 'errors/not_found', layout: 'layouts/application', status: 404 }
      format.all { render nothing: true, status: 404 }
    end
  end

  def render_500(exception)
    logger.info exception.backtrace.join("\n")
    respond_to do |format|
      format.html { render template: 'errors/internal_server_error', layout: 'layouts/application', status: 500 }
      format.all { render nothing: true, status: 500}
    end
  end

Since it’s using the application layout, you will see the error message you specify in the layout. The following is my 500 error page, /views/errors/internal_server_error.html.haml. For 404 page, you can also use @not_found_path instance variable in the view as well.

#errors-page
  .thumbnail.errors
    .errors-500
    .caption
      %h5 Sorry
  %h1 500 Internal Server Error

  - # Do not remove this line. It is used for development
  - # purposes. When an error is found. In development, it
  - # shows the logs, in production it is nil.
  .error-msg
    = @log

Cheers,

Best way to learn is to imitate (and how it should apply to startups in emerging markets)

Recently there were interesting articles beng circulated and discussed in the valley. One is Black Swan Farming by Paul Graham and the other one is Screw the Black Swans by Dave McClure. Interestingly enough, the fireside chat with Vinod Khosla at the TechCrunch Disrupt SF 2012 was a timely interview and speaks to the different viewpoints.

I suspected, but was surprised to learn that out of all YC companies, Airbnb and Dropbox account for three quarters (3/4) in terms of about $10 Billion valuation. Paul was saying how difficult it is to pick a game changer winner. What makes it more difficult is how great ideas seem like bad ideas in the beginning. If an idea looks good, then everyone including large companies will work on it, and startups will have even less chance of succeeding. It’s when an idea seems bad and thus hard time getting funded, but at the end succeeds, it’s a game changer (homerun).

Vinod Khosla said pretty much the same. His fund looks for companies that will make tremendous impact when successful. It doesn’t matter when they fail. Founders will move on to a new project. But they don’t want to invest in companies that will make a little to no impact when successful. Again, Vinod is looking for a game changer (homerun) company.

Dave on the other hand is saying that 500 startups focus on Ichiro’s of the world (consistent hitting) rather than Barry Bond’s of the world (homerun king). He goes further into discussing the differences between YC and 500 startups (like hackers vs. hustlers), but my main takeaway was what kind of companies they were looking to fund.

These discussions made me think about how startups in emerging markets (including Korea) could do better. When babies are born, they learn mostly by imitating people around them. There is also an old saying, “Imitation is the best form of flattery”. Many amateur athletes also learn by imitating professional players (watching and learning). Korea is the only country I know best outside the US, and I always noticed the lack of virtuous seed funding cycle in Korea – successful founders seed investing in other startups. Once startup is up and running, and shows notable tractions, they can raise money from VCs and the process and valuation will be much better. However, startups often fail even before they could show traction because either they make multiple mistakes or run out of money before they can pivot. Without proper seed funding (and mentoring), they will have really hard time reaching the traction point.

At any rate, I think the best way for startups in Korea (or in emerging markets) to produce good enough winners to start the virtuous cycle is to imitate successful US startups in Korea (or in emerging markets), and after having a few successful exits, the founders can help other founders by investing and mentoring them. Furthermore, after successful exits, those founders would be in much better position to take bigger risk and attempt to make big impacts. In baseball analogy, it would be something along the line of having a few good seasons of hitting consistently, then you can swing for homeruns. No doubt those good seasons would be a confidence builder as well.

I keep hearing how there are many incubators and accelerators popping up in Korea. Someone recently expressed a concern that it’s like a startup bubble and a few failures will have devastating effect on the whole startup movements. I think this type of imitation strategy will be good at least in the beginning. After there are good number of successful and experienced founders, they would be better equipped to make sounding decisions and also to help other up-and-coming founders. It would truly strengthen startup community in Korea (or in emerging markets).

Install ImageMagick on Lion from source

I’ve had a lot of problems trying to install ImageMagick using brew. Use the following series of commands to install from source. Got this from Stackoverflow answer.

cd /tmp
curl -OL ftp://ftp.imagemagick.org/pub/ImageMagick/ImageMagick.tar.gz
tar -xzf ImageMagick.tar.gz
cd ImageMagick-[VERSION]/
./configure --prefix=/usr/local --disable-static --with-modules --without-perl --without-magick-plus-plus --with-quantum-depth=8 --disable-openmp --with-gs-font-dir=/usr/local/share/ghostscript/fonts
make
sudo make install

How can you not get romantic about startups?

Recently I watched Moneyball – a non-fiction movie about how Billy Beane at Oakland A’s used statistics to get most under-valued players and create the most valued (least amount spent per game) team in MLB. If you haven’t watched it, I strongly recommend it. I don’t usually shed tears watching a movie, but I did watching Moneyball. Moneyball! It wasn’t a Oscar-worth drama. Nor was it gut-wrenching tear jerker. But why did I cry watching the movie?

*SPOILER ALERT!*

Because it was about one man’s lonely, uncertain fight against status quo under heavy pressure and criticism. Once a promising baseball player, Billy turned down a full scholarship to Stanford to join New York Mets as first-round draft for $125K. But he never became a top baseball player his scouts had predicted. He went through a number of minor leagues and other major league teams with not much to show for. He ended up at Oakland A’s, and he asked to become a scout. Later he became an assistant GM and a GM.

His push to use statistical analysis to pick players was controversial to say the least. He picked players against professional scouts’ advice. The scene where he and another coach visiting Scott Hatteberg was touching, since you could tell from his house and his reaction afterwards, he probably thought he could never play baseball again, but he was so grateful for having been given another chance. First 40 or so games, Oakland A was dead last in its division. Pressure and criticism were directed to him from all directions. It looked like he was divorced and had a daughter who was staying with his ex-wife. He tried to hide the pain he was going through, but the daughter could still see everything was not quite alright.

Then, the team started winning and became the first in its division. It had 19-game winning streak. On the game where they could have 20-game winning streak, they blew 11-0 lead at 11-11 tie. Then, Scott Hatteberg, the guy all baseball teams had written off as too injured to play, stepped up and hit a walk-off home run. They achieved 20-game winning streak.

What’s more touching is when Billy turned down Red Sox’s $12.5M offer to stay with Oakland A’s because his decision to go with New York Mets was his last decision made based on money.

Also, another notable thing he said is “How can you not get romantic about baseball?” after some dramatic finish.

It may have to do with my personality – always rooting for underdogs, preferring hole-in-the-wall or local places to established places, etc., but the whole movie was touching to me because of its resemblance to startups. Not all startups are made up of under-valued coders/marketers, but I believe some are. Somewhere in a corporate world, there would be some who think their skills and opinions aren’t properly appreciated (not financially). I wouldn’t call entrepreneurs misfits, but they are definitely different. They know the odds of success is low (1 out of 10 or less), but they keep going against all odds, criticism, roller coaster emotions, and tremendous pressure. Most of them are in it to challenge status-quo and traditional way of doing things, and to disrupt the market. Often times, they are going against huge corporations with lots of cash. When one makes it, it’s a home run (but not always). Sometimes they will turn down a big offer to keep moving ahead because they believe in the products, company and the team. Many startups, whether they make it or not, have dramatic stories behind them.

Seriously, How can you not get romantic about startups?

Simple Ruby on Rails app using Twilio API

Twilio created cloud telephony services made mainly for developers. I’ve been meaning to figure out how to use their service, and I had a chance to develop a Ruby on Rails app using Twilio app called Phonein. You can check out the source code here. It’s nothing fancy, and totally not optimized. I also used Twitter’s Bootstrap CSS/JS package.

While Twilio had a few samples for Ruby on Rails, it was kind of outdated and a few things were incorrect. I wanted to share what I did to use Twilio API to help you come up to speed quickly.

Basically my app is for home-care professionals, and it allows them to check in and out using client’s phone. For Twilio, the followings are its requirements.

  • Identify client by phone number
  • Identify home-care professional by 6-digit identification code
  • Check in the home-care professional at the client’s location
  • Read out tasks for the home-care professional
  • Check out the home-care professional from the client’s location

Twilio has its own markup language called TwiML, and in Twilio controller I created, TwiML is used to create views (or voices). I also created certain POST actions under Twilio controller that will receive input from phone (digits punched in by a person) and use it to look up information.

1. Answer incoming call

When you sign up with Twilio, you can configure destination URL Twilio will invoke when it receives an incoming call. For mine, it would be “http://phonein.herokuapp.com/twilios/incoming”. So, I have “incoming” action defined in “Twilios” controller.

Controller in app/controller/twilios_controller.rb

Just like a regular Ruby on Rails app, controller gets objects ready for the view. It looks up client using the incoming phone number. If client is not found, it says (instead of displaying) an error message and hangs up. If client is found, it asks user to enter a 6-digit code. @post_to object contains URL that will be invoked after user interacts with Twilio. In our case, it’s after user enters code.

  
class TwiliosController < ApplicationController      
  BASE_URL = "http://phonein.herokuapp.com/twilios"         

  def incoming          
    # Get client by phone number          
    client_phone = params['From'][2..params['From'].size]          
    @client = Client.find_by_phone(client_phone)               
    
    if @client.nil?               
      render :action => "no_client.xml.builder"
    else
      @post_to = BASE_URL + "/verify?client_id=#{@client.id}"
      render :action => "incoming.xml.builder", :layout => false
    end
  end
end

View in app/views/twilios/incoming.xml.builder

xml.instruct!
xml.Response do
  xml.Gather(:action => @post_to, :numDigits => 6) do
    xml.Say "Welcome to #{@client.name}'s residence. Please enter your 6 digit code."
  end
end

View in app/views/twilios/no_client.xml.builder

xml.instruct!
xml.Response do
  xml.Say "Client could not be found."
  xml.Hangup
end

2. Verifies home-care professional, checks in, and reads tasks.

Next, look up the home-care professional by the 6-digit code. The numbers user punched will be in ‘Digits’ parameter. If user is found but has not checked in before, it checks in the user and gives some options. If user has already checked in before, user is presented with a few options including an option to check out.

Controller in app/controller/twilios_controller.rb

In addition to incoming action.

class TwiliosController < ApplicationController   

  def verify     
    @client = Client.find(params[:client_id])         
    @agent = Agent.find_by_code(params['Digits'])          
    if @agent.nil?       
      # If no agent is found, say "no agent found" error message and hang up.       
      @post_to = BASE_URL + "/verify?client_id=#{@client.id}"       
      render :action => "no_agent.xml.builder"
      return
    else
      if @agent.checked_in?(@client.id) 
        @message = "You have already checked in."
      else
        @agent.check_in(@client.id)
        @message = "Now you are checked in."
      end
    end 

    # Default action is direction
    @post_to = BASE_URL + "/direction?agent_id=#{@agent.id}&client_id=#{@client.id}"
    render :action => "direction.xml.builder", :layout => false
  end
end

View in app/views/twilios/direction.xml.builder

xml.instruct!
xml.Response do
  xml.Gather(:action => @post_to, :numDigits => 1) do
    xml.Say "Welcome #{@agent.name}."
    xml.Say "#{@message}"
    xml.Say "Please press 1 to read the task. Press 2 to check out. Press 3 to hear about Yang. Or Press 4 to hang up."
  end
end

View in app/views/twilios/no_agent.xml.builder

xml.instruct!
xml.Response do
  xml.Gather(:action => @post_to, :numDigits => 6) do  
    xml.Say "Agent could not be found. Please enter your 6 digit code."
  end
end

3. Read tasks, check out or hear about Yang

In addition to incoming and verify actions.

This is where main messages are configured and played. Depending on the option user chooses, it will either 1) say the tasks again, 2) check the user out, 3) say a few things about Yang, or 4) just hang up.

class TwiliosController < ApplicationController   

  def direction         
    @client = Client.find(params[:client_id])     
    @agent = Agent.find(params[:agent_id])     
    @message = @client.task_list     
    @post_to = BASE_URL + "/direction?agent_id=#{@agent.id}&client_id=#{@client.id}"          

    # 1 to hear the tasks again, 2 to check out, 3 to hang up.     
    if params['Digits'] == '1'       
      render :action => "direction.xml.builder", :layout => false
    elsif params['Digits'] == '2'
      @agent.check_out(@client.id)
      @goodbye_message = "Thank you for your service today."
      render :action => 'goodbye.xml.builder', :layout => false
    elsif params['Digits'] == '3'
      @message = "Yang is the most awesome guy ever - both personally and professionally. He is pretty sexy, too."
      render :action => 'direction.xml.builder', :layout => false
    elsif params['Digits'] == '4'
      @goodbye_message = "Have a great day."
      render :action => 'goodbye.xml.builder', :layout => false
    end
  end
end

View in app/views/twilios/goodbye.xml.builder

xml.instruct!
xml.Response do
  xml.Say "#{@goodbye_message}"
  xml.Say "Good-bye."
  xml.Hangup
end

Have fun!

That’s it! My codes are so not refactored and optimized. I did it to see what I can do with Twilio app. I hope this gives you a tip of what you can do with Twilio API. Go bananas!

Facebook Connect with Rails (using Omniauth and Devise) [Update]

This is an update to my last post about Facebook Connect with a Rails app. At the time I was using facebooker plugin (yeah, a plugin, not a gem), which has been discontinued for the longest time, and thus wouldn’t work with current Facebook connect.

Since then, I’ve used omniauth, omniauth-facebook, and devise gems to implement Facebook connect with a few Rails app I have been toying with. So, this is kind of an update to my last post about integrating Facebook Connect with a Rails app.

1. First, you need the following gems in your Gemfile.

gem 'devise'
gem 'omniauth'
gem 'omniauth-facebook'
gem 'oauth2'

Make sure you install them by running “bundle install” command.

2. Next configure devise gem.

rails generate devise:install

3. Apply devise to a model. 99.9% of time, this would be the User model.

rails generate devise User

4. Next, generate authentication model with the following columns. Token column is extra, if you want to save an access token.

rails g model Authentication user_id:integer provider:string uid:string token:string

5. Configure omniauth by modifying config/initializers/omniauth.rb

Rails.application.config.middleware.use OmniAuth::Builder do
  # The following is for facebook
  provider :facebook, [APP ID], [SECRET KEY], {:scope => 'email, read_stream, read_friendlists, friends_likes, friends_status, offline_access'}

  # If you want to also configure for additional login services, they would be configured here.
end

6. After user authenticates with whatever provider you specify, user needs to be redirected to omniauth call, so add the following line in your routes.rb.

match '/auth/:provider/callback' => 'authentications#create'

7. Then in Authentications controller, you figure out whether to create a new user or log the user in, if the user is an existing user. For complete hash, take a look at omniauth-facebook github page.

def create
  auth = request.env["omniauth.auth"]

  # Try to find authentication first
  authentication = Authentication.find_by_provider_and_uid(auth['provider'], auth['uid'])

  if authentication
    # Authentication found, sign the user in.
    flash[:notice] = "Signed in successfully."
    sign_in_and_redirect(:user, authentication.user)
  else
    # Authentication not found, thus a new user.
    user = User.new
    user.apply_omniauth(auth)
    if user.save(:validate => false)
      flash[:notice] = "Account created and signed in successfully."
      sign_in_and_redirect(:user, user)
    else
      flash[:error] = "Error while creating a user account. Please try again."
      redirect_to root_url
    end
  end
end

8. In User model, store essential information with apply_omniauth method.

has_many :authentications, :dependent => :delete_all
def apply_omniauth(auth)
  # In previous omniauth, 'user_info' was used in place of 'raw_info'
  self.email = auth['extra']['raw_info']['email']
  # Again, saving token is optional. If you haven't created the column in authentications table, this will fail
  authentications.build(:provider => auth['provider'], :uid => auth['uid'], :token => auth['credentials']['token'])
end

9. In Authenication model,

belongs_to :user

10. In your view, user clicking on /auth/facebook/ link will be redirected to Facebook to log in.

<%= link_to 'Login with Facebook', '/auth/facebook/' %>

11. This method doesn’t do FB login in a popup. For that, you have to use FB Javascript SDK, and you can use the example here.

How to install Ubuntu on Chomebook (CR-48) and put parental control

I have had a couple of first-generation Chromebook (CR-48) for a while. I thought these would be pretty good portable computing devices to give to my kids. However, I wasn’t ready to do so unless there was some sort of parental control. As someone who spends a lot of time online, it’s too rough place for kids to roam around. I have known about their support for dev mode, and I decided to figure out which linux flavor would support CR-48.

First search attempt turned out Ubuntu, and since Ubuntu is known for better UI, I decided to give it a try. I also found out there is an excellent parental control guide on Ubuntu as well.

Install Ubuntu on Chromebook (CR-48)

There is no need to re-invent the wheel. Follow instruction here to put Ubuntu 11.04 on Chromebook (CR-48). It worked out like a charm.

Parental Control on Ubuntu

There is a great guide here, and I couldn’t get the Web Content Control to work. However, timekpr is pretty good. You should at least install that.

For filtering content, ProCon Latte Content Filter Firefox Add-on has been working pretty well.

So far, I have been happy with Ubuntu on CR-48. It’s slow and keypad doesn’t work too well, but I think kids are just happy to have their own laptops. :)

How to install KidsRuby on Mac OS X and Ubuntu

I have two kids, and I have been wanting to find ways to introduce my kids to programming. Especially for my son, who has asperger syndrome. I thought programming would be a good way to use his interests in logic and mathematics to good use.

I was so much in joy when I found KidsRuby. It’s an awesome project. I think Ruby is a great first language because of its elegance and object-oriented nature to the core.

Previously I had a hard time installing KidsRuby my Mac OS X, due to qt installation error. I was able to at least get it going by cloning their git repository. I had filed a bug for the qt installation error, and the author responded some time ago, but I hadn’t had time to verify it.

I finally had time to verify it, and it worked like a charm.

I also had converted two first-generation Google Chromebooks to use Ubuntu to give to my kids. And while I was familiar with Fedora, this was my first time using Ubuntu, and as new user, I had to search around the net to install Ruby and to install KidsRuby. I am close to finishing it, and I will describe it here.

Mac OS X

This one is pretty easy. Just download the installer dmg file from here, and follow the instruction. It will take a while, but it would be well worth it. You can find the KidsRuby folder in Application folder.

Ubuntu

Now, this one took a while. If you have Ruby installed already, you are good to go. If not, follow the direction below.

If Ruby isn’t installed

You can simply follow the direction from Ryan Bigg to install Ruby 1.9.3.

If Ruby is already installed

I am not sure which version of Ruby is supported, but this applies to Ruby 1.9.3.

The biggest problem I faced was the problem with ffi gem. I kept getting the following error.

Installing ffi (1.0.10) with native extensions
Gem::Installer::ExtensionBuildError: ERROR: Failed to build gem native extension.

        /usr/bin/ruby1.9.1 extconf.rb
:29:in `require': no such file to load -- mkmf (LoadError)
	from :29:in `require'
	from extconf.rb:4:in `'

Gem files will remain installed in /usr/share/kidsruby/ffi/ruby/1.9.1/gems/ffi-1.0.10 for inspection.
Results logged to /usr/share/kidsruby/ffi/ruby/1.9.1/gems/ffi-1.0.10/ext/ffi_c/gem_make.out
An error occured while installing ffi (1.0.10), and Bundler cannot continue.
Make sure that `gem install ffi -v '1.0.10'` succeeds before bundling.

After searching the net for a while, I realized that I needed to install “-dev” package as well. Since I am using 1.9.3, I had to install ruby1.9.1-dev.

sudo apt-get install ruby1.9.1-dev

After that you can clone the git repository and install necessary packages.

git clone https://github.com/hybridgroup/kidsruby.git

cd kidsruby

sudo apt-get install libqt4-dev
sudo apt-get install cmake
gem install qtbindings
bundle install

The bundle install part will take a while.

For the lesson part, I had a surprising result – my son totally lost interest very quickly, but my daughter was more into it. :)

KidsRuby on Ubuntu

Never never give up on your life

When I first read TechCrunch’s article on Diaspora co-founder Ilya Zhitomirskiy’s death, I didn’t think much of it except that it didn’t mention anything about the cause of the death. It usually means only thing, and my suspicion was confirmed by hacker news thread.

http://news.ycombinator.com/item?id=3231531

What I particularly noticed about the thread was discussion of failure and stress of founding a startup and other suicides by very smart folks. It also reminded me of earlier tragic passing of a co-founder of a Y-Combinator-funded company and the article I read on WIRED magazine about two AI scientists committing suicides almost in identical ways.

I would never know why these guys did what they did. But for me, I have one thing that would prevent me from thinking about it. It’s my kids and my family to an extent. It’s a double-edged sword. On one side, it’s the reason for not being able to take huge risk, but on the other side, it’s the reason for my sanity no matter how shitty my life is at any given moment (and my life right now isn’t all that spectacular).

Also, we should also keep things in perspective. In grand scheme of this universe, we are just small part of green/blue spec called Earth. You shouldn’t care about and be afraid of failures/rejections. Who cares? People will forget and move on. I think it’s courageous and commendable to just try. Regardless of outcome, having tried something sets you ahead of many others.

Just remember the following quotes.

“Regret for the things we did can be tempered by time; it is regret for the things we did not do that is inconsolable.”

Also, especially this one.

“It is not the critic who counts; not the man who points out how the strong man stumbles, or where the doer of deeds could have done them better. The credit belongs to the man who is actually in the arena, whose face is marred by dust and sweat and blood; who strives valiantly; who errs, who comes short again and again, because there is no effort without error and shortcoming; but who does actually strive to do the deeds; who knows great enthusiasms, the great devotions; who spends himself in a worthy cause; who at the best knows in the end the triumph of high achievement, and who at the worst, if he fails, at least fails while daring greatly, so that his place shall never be with those cold and timid souls who neither know victory nor defeat.”