<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>KS Sreeram&#x27;s Blog</title>
    <link rel="self" type="application/atom+xml" href="https://kssreeram.org/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://kssreeram.org"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2022-06-05T00:00:00+00:00</updated>
    <id>https://kssreeram.org/atom.xml</id>
    <entry xml:lang="en">
        <title>Scroll-jacking for Fun and Math</title>
        <published>2022-06-05T00:00:00+00:00</published>
        <updated>2022-06-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kssreeram.org/blog/scrolljacking-fun-math/"/>
        <id>https://kssreeram.org/blog/scrolljacking-fun-math/</id>
        
        <content type="html" xml:base="https://kssreeram.org/blog/scrolljacking-fun-math/">&lt;h2 id=&quot;pacing-and-visual-communication&quot;&gt;Pacing and Visual Communication&lt;&#x2F;h2&gt;
&lt;p&gt;I love YouTube. Videos can be great at visual communication.
They&#x27;re good for conveying a high-level overview of a topic.&lt;&#x2F;p&gt;
&lt;p&gt;But they aren&#x27;t great for communicating detail-oriented information.
Such information needs to be digested by the viewer, and that takes time.
Time that varies a lot. It varies from person to person.
It can also vary across different parts of the content for a single person.&lt;&#x2F;p&gt;
&lt;p&gt;Inevitably, you&#x27;ll find videos too slow, too fast, or both.
You don&#x27;t control the pacing.&lt;&#x2F;p&gt;
&lt;p&gt;Before you ask.. No, setting the playback speed to 1.5x is not good enough. :&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;This is where text articles (papers, blog posts) shine.
You have &lt;em&gt;complete&lt;&#x2F;em&gt; control over the pacing.
You can read one paragraph fast, the second one slow, and entirely skip a third.
You can go back and forth with ease.&lt;&#x2F;p&gt;
&lt;p&gt;But... text articles aren&#x27;t great at visual communication.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;an-experiment&quot;&gt;An Experiment&lt;&#x2F;h2&gt;
&lt;p&gt;Is it possible communicate visually while letting the viewer control the pacing?&lt;&#x2F;p&gt;
&lt;p&gt;Enter scroll-jacking! (hijacking the scroll behavior)&lt;&#x2F;p&gt;
&lt;p&gt;You might have seen websites that hijack the scroll behavior for a variety of gimmicks.
Many of these gimmicks are cringe-inducing and annoying.&lt;&#x2F;p&gt;
&lt;p&gt;If you look past the cringe, perhaps scroll-jacking can be put to good use.
Perhaps you can let the viewer control the pacing of an animation. Scrolling with modern phones (or a good trackpad) is smooth and easy.&lt;&#x2F;p&gt;
&lt;p&gt;Can we use this for communicating, say, math?&lt;&#x2F;p&gt;
&lt;p&gt;Here are a couple of experiments in doing just that.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;kssreeram.org&#x2F;blog&#x2F;pythagoras-theorem&#x2F;&quot;&gt;Pythagoras&#x27; Theorem&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;kssreeram.org&#x2F;blog&#x2F;sigma-n&#x2F;&quot;&gt;What is 1 + 2 + ⋯ (till n)?&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Admittedly, this is simple high-school math. Someday, I&#x27;d like to try something more complex.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;(@-me on twitter &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;kssreeram&quot;&gt;@kssreeram&lt;&#x2F;a&gt;
for comments and feedback.)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>What is 1 + 2 + ⋯ (till n)?</title>
        <published>2022-06-04T00:00:00+00:00</published>
        <updated>2022-06-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kssreeram.org/blog/sigma-n/"/>
        <id>https://kssreeram.org/blog/sigma-n/</id>
        
        <content type="html" xml:base="https://kssreeram.org/blog/sigma-n/">&lt;style type=&quot;text&#x2F;css&quot;&gt;
.math {
    font-weight: bold;
}
&lt;&#x2F;style&gt;
&lt;p&gt;The answer is:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;span class=&quot;math&quot;&gt;n × (n + 1) &#x2F; 2&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s check with an example, &lt;span class=&quot;math&quot;&gt;n = 4&lt;&#x2F;span&gt;.&lt;&#x2F;p&gt;
&lt;div class=&quot;math&quot;&gt;
1 + 2 + 3 + 4&lt;br&#x2F;&gt;
= 4 × (4 + 1) &#x2F; 2&lt;br&#x2F;&gt;
= 4 × 5 &#x2F; 2&lt;br&#x2F;&gt;
= 10
&lt;&#x2F;div&gt;
&lt;p&gt;Yep. That&#x27;s right.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s see if we can prove this...&lt;&#x2F;p&gt;
&lt;p&gt;(Continue scrolling to play the animation.
Please use a smooth scrolling device like a touchscreen or a trackpad.)&lt;&#x2F;p&gt;
&lt;div id=&quot;filmroll-1&quot; class=&quot;filmroll&quot;&gt;
    &lt;canvas class=&quot;canvas&quot; width=&quot;400&quot; height=&quot;400&quot;&gt;&lt;&#x2F;canvas&gt;
    &lt;div class=&quot;section-container&quot; data-timing=&quot;1.2&quot;&gt;
        &lt;div class=&quot;section-caption&quot;&gt;
            &lt;div class=&quot;section-caption-content&quot;&gt;
            Let&#x27;s represent
            &lt;span class=&quot;math&quot;&gt;1&lt;&#x2F;span&gt;
            to
            &lt;span class=&quot;math&quot;&gt;n&lt;&#x2F;span&gt;
            with balls.&lt;br&#x2F;&gt;
            &lt;&#x2F;div&gt;
        &lt;&#x2F;div&gt;
    &lt;&#x2F;div&gt;
    &lt;div class=&quot;section-container&quot; data-timing=&quot;0&quot;&gt;
        &lt;div class=&quot;section-caption&quot;&gt;
            &lt;div class=&quot;section-caption-content&quot;&gt;
            The number of yellow balls =&lt;br&#x2F;&gt;
            &lt;span class=&quot;math&quot;&gt;
            1 + 2 + 3 + ⋯ (&lt;&#x2F;span&gt;till
            &lt;span class=&quot;math&quot;&gt;
            n)&lt;&#x2F;span&gt;
            &lt;&#x2F;div&gt;
        &lt;&#x2F;div&gt;
    &lt;&#x2F;div&gt;
    &lt;div class=&quot;section-container&quot; data-timing=&quot;0.5&quot;&gt;
        &lt;div class=&quot;section-caption&quot;&gt;
            &lt;div class=&quot;section-caption-content&quot;&gt;
            Double the number of balls,&lt;br&#x2F;&gt;
            and make a grid.
            &lt;&#x2F;div&gt;
        &lt;&#x2F;div&gt;
    &lt;&#x2F;div&gt;
    &lt;div class=&quot;section-container&quot; data-timing=&quot;0.3&quot;&gt;
        &lt;div class=&quot;section-caption&quot;&gt;
            &lt;div class=&quot;section-caption-content&quot;&gt;
            The first row has
            &lt;span class=&quot;math&quot;&gt;1&lt;&#x2F;span&gt;
            yellow &amp;amp;
            &lt;span class=&quot;math&quot;&gt;n&lt;&#x2F;span&gt;
            blue balls.&lt;br&#x2F;&gt;
            A total of
            &lt;span class=&quot;math&quot;&gt;n + 1&lt;&#x2F;span&gt;
            balls.
            &lt;&#x2F;div&gt;
        &lt;&#x2F;div&gt;
    &lt;&#x2F;div&gt;
    &lt;div class=&quot;section-container&quot; data-timing=&quot;0.3&quot;&gt;
        &lt;div class=&quot;section-caption&quot;&gt;
            &lt;div class=&quot;section-caption-content&quot;&gt;
            The second row has
            &lt;span class=&quot;math&quot;&gt;2&lt;&#x2F;span&gt;
            yellow &amp;amp;
            &lt;span class=&quot;math&quot;&gt;n-1&lt;&#x2F;span&gt;
            blue balls.&lt;br&#x2F;&gt;
            Again, a total of
            &lt;span class=&quot;math&quot;&gt;n + 1&lt;&#x2F;span&gt;
            balls.
            &lt;&#x2F;div&gt;
        &lt;&#x2F;div&gt;
    &lt;&#x2F;div&gt;
    &lt;div class=&quot;section-container&quot; data-timing=&quot;0.3&quot;&gt;
        &lt;div class=&quot;section-caption&quot;&gt;
            &lt;div class=&quot;section-caption-content&quot;&gt;
            Every row has
            &lt;span class=&quot;math&quot;&gt;n + 1&lt;&#x2F;span&gt;
            balls!&lt;br&#x2F;&gt;
            The grid has a total of 
            &lt;span class=&quot;math&quot;&gt;
            n × (n + 1)
            &lt;&#x2F;span&gt;
            balls.
            &lt;&#x2F;div&gt;
        &lt;&#x2F;div&gt;
    &lt;&#x2F;div&gt;
    &lt;div class=&quot;section-container&quot; data-timing=&quot;0.3&quot;&gt;
        &lt;div class=&quot;section-caption&quot;&gt;
            &lt;div class=&quot;section-caption-content&quot;&gt;
            The number of yellow balls&lt;br&#x2F;&gt;
            is half that
            &lt;span class=&quot;math&quot;&gt;
            = n × (n + 1) &#x2F; 2
            &lt;&#x2F;span&gt;
            &lt;&#x2F;div&gt;
        &lt;&#x2F;div&gt;
    &lt;&#x2F;div&gt;
    &lt;div class=&quot;section-container&quot; data-timing=&quot;0&quot;&gt;
        &lt;div class=&quot;section-caption&quot;&gt;
            &lt;div class=&quot;section-caption-content&quot;&gt;
            Therefore,&lt;br&#x2F;&gt;
            &lt;span class=&quot;math&quot;&gt;
            1 + 2 + ⋯ (&lt;&#x2F;span&gt;till
            &lt;span class=&quot;math&quot;&gt;n)
            &amp;nbsp;=&amp;nbsp; n × (n + 1) &#x2F; 2
            &lt;&#x2F;span&gt;
            &lt;&#x2F;div&gt;
        &lt;&#x2F;div&gt;
    &lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;&lt;em&gt;(@-me on twitter &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;kssreeram&quot;&gt;@kssreeram&lt;&#x2F;a&gt;
for comments and feedback.)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;script&gt;

&#x2F;&#x2F;
&#x2F;&#x2F; Circle
&#x2F;&#x2F;

function Circle(center, radius, color) {
    this.center = center;
    this.radius = radius;
    this.color = color;
}

Circle.prototype.execute = function (ctx) {
    let center = deref(this.center);
    let radius = deref(this.radius);
    let color = deref(this.color);
    ctx.beginPath();
    ctx.arc(center.x, center.y, radius, 0, 2 * Math.PI);
    ctx.fillStyle = color.toString();
    ctx.fill();
};


&#x2F;&#x2F;
&#x2F;&#x2F; COLORS
&#x2F;&#x2F; FONT1
&#x2F;&#x2F; FONT2
&#x2F;&#x2F;

const COLORS = {
    bg: parseColor(&quot;#fcf3d9&quot;),
    red: parseColor(&quot;#c1251f&quot;),
    yellow: parseColor(&quot;#f8c02d&quot;),
    blue: parseColor(&quot;#0d5073&quot;),
    black: parseColor(&quot;#000000&quot;),
};

const FONT1 = &quot;16px sans-serif&quot;;

const FONT2 = &quot;12px sans-serif&quot;;


&#x2F;&#x2F;
&#x2F;&#x2F; drawSigmaN
&#x2F;&#x2F;

function drawSigmaN(target, playing) {
    let currentSection = 0;
    let animate = function (fStart, fEnd, vStart, vEnd) {
        if (playing.section &lt; currentSection) return vStart;
        if (playing.section &gt; currentSection) return vEnd;
        let f = (playing.fraction - fStart) &#x2F; (fEnd - fStart);
        f = clamp(f, 0.0, 1.0);
        return lerp(f, vStart, vEnd);
    };

    &#x2F;&#x2F; Collect draw commands.
    let commands = [];
    let cmd = function (obj) {
        commands.push(obj);
    };

    &#x2F;&#x2F; Draw BG and border.
    {
        let [w, h] = [target.requiredWidth, target.requiredHeight];
        cmd(new Rect(vec2(0, 0), vec2(w, h), COLORS.bg));
        cmd(new RectBorder(vec2(0, 0), vec2(w, h), 3, COLORS.yellow));
    }

    &#x2F;&#x2F; Draw balls for each of 1 to n.
    let N = 6;
    let RSTEP = 22;
    assert(N &gt;= 4);
    let R = RSTEP - 4;
    let ballsOrigin = vec2(65, 90);
    &#x2F;&#x2F; Draw highlighter. This is used later.
    let highlightP1 = ref(vec2(-RSTEP - 5, -RSTEP));
    let highlightP2 = ref(vec2((2*N+1)*RSTEP + 5, RSTEP));
    let highlightAlpha = ref(0.0);
    {
        cmd(new Save());
        cmd(new Translate(ballsOrigin));
        cmd(new SetAlpha(highlightAlpha));
        cmd(new Rect(highlightP1, highlightP2, parseColor(&quot;#FFFFFF&quot;)));
        cmd(new RectBorder(highlightP1, highlightP2, 2, COLORS.red));
        cmd(new Restore());
    }
    {
        let steps = 3 * 2 + 1 + 1;
        let step = 0;
        cmd(new Save());
        cmd(new Translate(ballsOrigin));
        let startColor = lerp(0.1, COLORS.bg, COLORS.yellow);
        let rowP = vec2(0, 0);
        let rowLabelCommands = [];
        for (let row = 1; row &lt;= N; ++row) {
            &#x2F;&#x2F; Draw label.
            let text = `${row}`;
            if (row === N - 2) { text = &quot;⋮&quot;; }
            else if (row === N-1) { text = &quot;n-1&quot;; }
            else if (row === N) { text = &quot;n&quot;; }
            {
                let si = Math.min(step, steps - 1);
                let scale = animate(si&#x2F;steps, (si + 1)&#x2F;steps, 30, 1);
                let alpha = animate(si&#x2F;steps, (si + 0.9)&#x2F;steps, 0.0, 0.3);
                alpha = animate((si + 0.9)&#x2F;steps, (si + 1)&#x2F;steps, alpha, 1.0);
                rowLabelCommands.push(new Save());
                rowLabelCommands.push(new SetAlpha(alpha));
                rowLabelCommands.push(new Translate(rowP));
                if (row &lt; N - 2) {
                    rowLabelCommands.push(new Scale1(scale));
                }
                rowLabelCommands.push(new Label(vec2(0, 0), text, COLORS.black, FONT1));
                rowLabelCommands.push(new Restore());
            }
            step += 1;

            &#x2F;&#x2F; Draw row of balls.
            {
                let ballP = rowP;
                let si = Math.min(step, steps - 1);
                let [t1, t2] = [si&#x2F;steps, (si + 1)&#x2F;steps];
                let color = animate(t1, t2, startColor, COLORS.yellow);
                for (let i = 0; i &lt; row; ++i) {
                    cmd(new Circle(ballP, R, color));
                    ballP = ballP.add(vec2(2*RSTEP, 0));
                }
            }
            step += 1;
            rowP = rowP.add(vec2(0, 2*RSTEP));
        }
        for (let obj of rowLabelCommands) {
            cmd(obj);
        }
        cmd(new Restore());
    }
    currentSection += 1;

    &#x2F;&#x2F; Nothing to do for section 2.
    currentSection += 1;

    &#x2F;&#x2F; Double the balls and make a grid.
    let blueAlpha = ref(0.0);
    {
        let p1 = vec2(-RSTEP, -RSTEP);
        let p2 = p1.add(vec2((N + 1)*2*RSTEP, N*2*RSTEP));
        let mid = p1.add(p2).scale(0.5);
        blueAlpha.value = animate(0.0, 0.1, blueAlpha.value, 0.8);
        blueAlpha.value = animate(0.1, 1.0, blueAlpha.value, 1.0);
        let angle = animate(0.0, 1.0, 0, -180);
        cmd(new Save());
        cmd(new SetAlpha(blueAlpha));
        cmd(new Translate(ballsOrigin));
        cmd(new Translate(mid));
        cmd(new Rotate(angle));
        cmd(new Translate(mid.negate()));
        let rowP = vec2(0, 0);
        for (let row = 1; row &lt;= N; ++row) {
            &#x2F;&#x2F; Draw row of balls.
            let ballP = rowP;
            for (let i = 0; i &lt; row; ++i) {
                cmd(new Circle(ballP, R, COLORS.blue));
                ballP = ballP.add(vec2(2*RSTEP, 0));
            }
            let text = `${row}`;
            if (row === N - 2) { text = &quot;⋮&quot;; }
            else if (row === N-1) { text = &quot;n-1&quot;; }
            else if (row === N) { text = &quot;n&quot;; }
            cmd(new Save());
            cmd(new Translate(rowP));
            cmd(new Rotate(-angle));
            cmd(new Label(vec2(0, 0), text, COLORS.bg, FONT1));
            cmd(new Restore());
            rowP = rowP.add(vec2(0, 2*RSTEP));
        }
        cmd(new Restore());
    }
    currentSection += 1;

    &#x2F;&#x2F; Highlight row 1.
    {
        highlightAlpha.value = animate(0.0, 0.5, highlightAlpha.value, 1.0);
    }
    currentSection += 1;

    &#x2F;&#x2F; Highlight row 2.
    {
        let p1 = vec2(-RSTEP - 5, RSTEP);
        let p2 = vec2((2*N+1)*RSTEP + 5, 3*RSTEP);
        highlightP1.value = animate(0.0, 0.5, highlightP1.value, p1);
        highlightP2.value = animate(0.0, 0.5, highlightP2.value, p2);
    }
    currentSection += 1;

    &#x2F;&#x2F; Highlight all rows.
    {
        let p1 = vec2(-RSTEP - 5, -RSTEP);
        let p2 = vec2((2*N+1)*RSTEP + 5, (2*N-1)*RSTEP);
        highlightP1.value = animate(0.0, 0.5, highlightP1.value, p1);
        highlightP2.value = animate(0.0, 0.5, highlightP2.value, p2);
    }
    currentSection += 1;

    &#x2F;&#x2F; Show only the yellow balls.
    {
        highlightAlpha.value = animate(0.0, 1.0, highlightAlpha.value, 0.0);
        blueAlpha.value = animate(0.0, 1.0, blueAlpha.value, 0.1);
    }
    currentSection += 1;

    &#x2F;&#x2F; Execute draw commands.
    let ctx = target.context;
    ctx.save();
    {
        let f = target.scale * target.pixelRatio;
        ctx.scale(f, f);
    }
    for (let obj of commands) {
        obj.execute(ctx);
    }
    ctx.restore();
}


&#x2F;&#x2F;
&#x2F;&#x2F; updateLoop
&#x2F;&#x2F; initialize
&#x2F;&#x2F;

function updateLoop(froll) {
    froll.update();
    requestAnimationFrame(function () { updateLoop(froll); });
}

function initialize() {
    let root = document.getElementById(&quot;filmroll-1&quot;);
    let froll = new Filmroll(root, drawSigmaN);
    updateLoop(froll);
}

document.addEventListener(&quot;DOMContentLoaded&quot;, initialize);
&lt;&#x2F;script&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Pythagoras&#x27; Theorem</title>
        <published>2022-06-03T00:00:00+00:00</published>
        <updated>2022-06-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kssreeram.org/blog/pythagoras-theorem/"/>
        <id>https://kssreeram.org/blog/pythagoras-theorem/</id>
        
        <content type="html" xml:base="https://kssreeram.org/blog/pythagoras-theorem/">&lt;p&gt;You might remember Pythagoras&#x27; theorem from high-school math:&lt;&#x2F;p&gt;
&lt;p&gt;In a right-angled triangle, the square of the hypotenuse
is the sum of the squares of the other two sides.&lt;&#x2F;p&gt;
&lt;p&gt;The following is a visual &quot;proof&quot; of the theorem:&lt;&#x2F;p&gt;
&lt;p&gt;(Continue scrolling to play the animation.
Please use a smooth scrolling device like a touchscreen or a trackpad.)&lt;&#x2F;p&gt;
&lt;style type=&quot;text&#x2F;css&quot;&gt;
.math {
    font-family: &quot;Georgia&quot;, serif;
    font-style: italic;
    font-weight: bold;
}
&lt;&#x2F;style&gt;
&lt;div id=&quot;filmroll-1&quot; class=&quot;filmroll&quot;&gt;
    &lt;canvas class=&quot;canvas&quot; width=&quot;400&quot; height=&quot;400&quot;&gt;&lt;&#x2F;canvas&gt;
    &lt;div class=&quot;section-container&quot; data-timing=&quot;0.3&quot;&gt;
        &lt;div class=&quot;section-caption&quot;&gt;
            &lt;div class=&quot;section-caption-content&quot;&gt;
            Consider a right-angled triangle &lt;br&#x2F;&gt;
            with sides
            &lt;span class=&quot;math&quot;&gt;a&lt;&#x2F;span&gt;,
            &lt;span class=&quot;math&quot;&gt;b&lt;&#x2F;span&gt;, and
            &lt;span class=&quot;math&quot;&gt;c&lt;&#x2F;span&gt;.
            &lt;&#x2F;div&gt;
        &lt;&#x2F;div&gt;
    &lt;&#x2F;div&gt;
    &lt;div class=&quot;section-container&quot; data-timing=&quot;0.3&quot;&gt;
        &lt;div class=&quot;section-caption&quot;&gt;
            &lt;div class=&quot;section-caption-content&quot;&gt;
            Make a square of side
            &lt;span class=&quot;math&quot;&gt;a + b&lt;&#x2F;span&gt;.
            &lt;&#x2F;div&gt;
        &lt;&#x2F;div&gt;
    &lt;&#x2F;div&gt;
    &lt;div class=&quot;section-container&quot; data-timing=&quot;0.5&quot;&gt;
        &lt;div class=&quot;section-caption&quot;&gt;
            &lt;div class=&quot;section-caption-content&quot;&gt;
            Place 4 copies of the triangle in it.&lt;br&#x2F;&gt;
            They fit perfectly!
            &lt;&#x2F;div&gt;
        &lt;&#x2F;div&gt;
    &lt;&#x2F;div&gt;
    &lt;div class=&quot;section-container&quot; data-timing=&quot;0.5&quot;&gt;
        &lt;div class=&quot;section-caption&quot;&gt;
            &lt;div class=&quot;section-caption-content&quot;&gt;
            The visible blue is a square!&lt;br&#x2F;&gt;
            With side
            &lt;span class=&quot;math&quot;&gt;c&lt;&#x2F;span&gt;.
            &lt;&#x2F;div&gt;
        &lt;&#x2F;div&gt;
    &lt;&#x2F;div&gt;
    &lt;div class=&quot;section-container&quot; data-timing=&quot;0.5&quot;&gt;
        &lt;div class=&quot;section-caption&quot;&gt;
            &lt;div class=&quot;section-caption-content&quot;&gt;
            Its area is
            &lt;span class=&quot;math&quot;&gt;c²&lt;&#x2F;span&gt;.
            &lt;&#x2F;div&gt;
        &lt;&#x2F;div&gt;
    &lt;&#x2F;div&gt;
    &lt;div class=&quot;section-container&quot; data-timing=&quot;0.5&quot;&gt;
        &lt;div class=&quot;section-caption&quot;&gt;
            &lt;div class=&quot;section-caption-content&quot;&gt;
            Let&#x27;s re-arrange the triangles&lt;br&#x2F;&gt;
            without changing the blue area...
            &lt;&#x2F;div&gt;
        &lt;&#x2F;div&gt;
    &lt;&#x2F;div&gt;
    &lt;div class=&quot;section-container&quot; data-timing=&quot;0.5&quot;&gt;
        &lt;div class=&quot;section-caption&quot;&gt;
            &lt;div class=&quot;section-caption-content&quot;&gt;
            Rotate two of the triangles by 90°.
            &lt;&#x2F;div&gt;
        &lt;&#x2F;div&gt;
    &lt;&#x2F;div&gt;
    &lt;div class=&quot;section-container&quot; data-timing=&quot;0.9&quot;&gt;
        &lt;div class=&quot;section-caption&quot;&gt;
            &lt;div class=&quot;section-caption-content&quot;&gt;
            What remains are two squares.&lt;br&#x2F;&gt;
            With sides
            &lt;span class=&quot;math&quot;&gt;a&lt;&#x2F;span&gt;
            and
            &lt;span class=&quot;math&quot;&gt;b&lt;&#x2F;span&gt;.
            &lt;&#x2F;div&gt;
        &lt;&#x2F;div&gt;
    &lt;&#x2F;div&gt;
    &lt;div class=&quot;section-container&quot; data-timing=&quot;0.5&quot;&gt;
        &lt;div class=&quot;section-caption&quot;&gt;
            &lt;div class=&quot;section-caption-content&quot;&gt;
            Their areas are
            &lt;span class=&quot;math&quot;&gt;a²&lt;&#x2F;span&gt;
            and
            &lt;span class=&quot;math&quot;&gt;b²&lt;&#x2F;span&gt;.
            &lt;&#x2F;div&gt;
        &lt;&#x2F;div&gt;
    &lt;&#x2F;div&gt;
    &lt;div class=&quot;section-container&quot; data-timing=&quot;0&quot;&gt;
        &lt;div class=&quot;section-caption&quot;&gt;
            &lt;div class=&quot;section-caption-content&quot;&gt;
            The rotations didn&#x27;t change the blue area.&lt;br&#x2F;&gt;
            Therefore,
            &lt;span class=&quot;math&quot;&gt;c² = a² + b²&lt;&#x2F;span&gt;.
            &lt;&#x2F;div&gt;
        &lt;&#x2F;div&gt;
    &lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;&lt;em&gt;(@-me on twitter &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;kssreeram&quot;&gt;@kssreeram&lt;&#x2F;a&gt;
for comments and feedback.)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;script&gt;

&#x2F;&#x2F;
&#x2F;&#x2F; COLORS
&#x2F;&#x2F; FONT
&#x2F;&#x2F;

const COLORS = {
    bg: parseColor(&quot;#fcf3d9&quot;),
    red: parseColor(&quot;#c1251f&quot;),
    yellow: parseColor(&quot;#f8c02d&quot;),
    blue: parseColor(&quot;#0d5073&quot;),
    black: parseColor(&quot;#000000&quot;),
};

const FONT = &quot;italic 16px Georgia, serif&quot;;


&#x2F;&#x2F;
&#x2F;&#x2F; drawPythagoras
&#x2F;&#x2F;

function drawPythagoras(target, playing) {
    let currentSection = 0;
    let animate = function (fStart, fEnd, vStart, vEnd) {
        if (playing.section &lt; currentSection) return vStart;
        if (playing.section &gt; currentSection) return vEnd;
        let f = (playing.fraction - fStart) &#x2F; (fEnd - fStart);
        f = clamp(f, 0.0, 1.0);
        return lerp(f, vStart, vEnd);
    };

    &#x2F;&#x2F; Collect draw commands.
    let commands = [];
    let cmd = function (obj) {
        commands.push(obj);
    };

    &#x2F;&#x2F; Draw BG and border.
    {
        let [w, h] = [target.requiredWidth, target.requiredHeight];
        cmd(new Rect(vec2(0, 0), vec2(w, h), COLORS.bg));
        cmd(new RectBorder(vec2(0, 0), vec2(w, h), 3, COLORS.yellow));
    }

    &#x2F;&#x2F; Draw main triangle.
    let [a, b] = [110, 40];
    let tri0Origin = vec2(150, 80);
    let triPoints = [vec2(0, 0), vec2(a, 0), vec2(0, -b)];
    {
        let marker = [
            vec2(0,0), vec2(10, 0),
            vec2(10, -10), vec2(0, -10),
        ];
        cmd(new Save());
        cmd(new Translate(tri0Origin));
        cmd(new Polygon(triPoints, COLORS.yellow, 1, COLORS.red));
        cmd(new Polygon(marker, null, 1, COLORS.red));

        &#x2F;&#x2F; Draw labels.
        let ps = [
            pointAlongLine(triPoints[0], triPoints[1], 0.4, 13),
            pointAlongLine(triPoints[2], triPoints[0], 0.5, 13),
            pointAlongLine(triPoints[1], triPoints[2], 0.55, 13),
        ];
        let scale = animate(0.0, 1.0, 10, 1);
        let alpha = 0;
        alpha = animate(0.0, 0.8, alpha, 0.3);
        alpha = animate(0.8, 1.0, alpha, 1.0);
        cmd(new SetAlpha(alpha));
        for (let i = 0; i &lt; ps.length; ++i) {
            cmd(new Save());
            cmd(new Translate(ps[i]));
            cmd(new Scale1(scale));
            cmd(new Label(vec2(0, 0), (&quot;abc&quot;)[i], COLORS.black, FONT));
            cmd(new Restore());
        }
        cmd(new Restore());
    }
    currentSection += 1;

    &#x2F;&#x2F; Draw first square.
    let square1Origin = vec2(30, 170);
    let squarePoints = [vec2(0, 0), vec2(0, a+b), vec2(a+b, a+b), vec2(a+b, 0)];
    {
        let alpha = animate(0.0, 1.0, 0.0, 1.0);
        let p = pointAlongLine(vec2(a+b,-15), vec2(0,-15), 0.5, 15);
        cmd(new Save());
        cmd(new SetAlpha(alpha));
        cmd(new Translate(square1Origin));
        cmd(new Polygon(squarePoints, COLORS.blue, null, null));
        cmd(new ArrowLine(vec2(0, -15), vec2(a+b, -15), 1, COLORS.black));
        cmd(new Label(p, &quot;a + b&quot;, COLORS.black, FONT));
        cmd(new Restore());
    }
    currentSection += 1;

    &#x2F;&#x2F; Place 4 copies of the triangle inside the square.
    let triOrigins = [vec2(0, 0), vec2(0, a+b), vec2(a+b, 0), vec2(a+b, a+b)];
    let triRotations = [90, 0, -180, -90];
    let square1TriColor = ref(COLORS.yellow);
    {
        let alpha = 0;
        alpha = animate(0.0, 0.01, alpha, 0.5);
        alpha = animate(0.01, 1.0, alpha, 1.0);
        for (let i = 0; i &lt; triOrigins.length; ++i) {
            let destP = triOrigins[i].add(square1Origin);
            let p = animate(0.0, 1.0, tri0Origin, destP);
            let angle = animate(0.0, 1.0, 0, triRotations[i]);
            cmd(new Save());
            cmd(new SetAlpha(alpha));
            cmd(new Translate(p));
            cmd(new Rotate(angle));
            cmd(new Polygon(triPoints, square1TriColor, null, null));
            cmd(new Restore());
        }
    }
    currentSection += 1;

    &#x2F;&#x2F; Draw labels for the &quot;c&quot; square.
    let cAlpha = ref(0.0);
    {
        let ps = [
            pointAlongLine(vec2(0, a),   vec2(b, 0),   0.5, 10),
            pointAlongLine(vec2(b, 0),   vec2(a+b, b), 0.5, 10),
            pointAlongLine(vec2(a+b, b), vec2(a, a+b), 0.5, 10),
            pointAlongLine(vec2(a, a+b), vec2(0, a),   0.5, 10),
        ];
        cAlpha.value = animate(0.0, 1.0, cAlpha.value, 1.0);
        let scale = animate(0.0, 1.0, 30, 1);
        let color = animate(0.0, 1.0, COLORS.blue, COLORS.bg);
        let triDestColor = lerp(0.3, COLORS.bg, COLORS.yellow);
        square1TriColor.value = animate(0.0, 1.0, square1TriColor.value, triDestColor);
        for (let p of ps) {
            let p2 = p.add(square1Origin);
            cmd(new Save());
            cmd(new Translate(p2));
            cmd(new Scale1(scale));
            cmd(new SetAlpha(cAlpha));
            cmd(new Label(vec2(0, 0), &quot;c&quot;, color, FONT));
            cmd(new Restore());
        }
    }
    currentSection += 1;

    &#x2F;&#x2F; Draw the c^2 label.
    {
        let alpha = animate(0.0, 1.0, 0.0, 1.0);
        let scale = animate(0.0, 1.0, 30, 1);
        let color = animate(0.0, 1.0, COLORS.blue, COLORS.bg);
        square1TriColor.value = animate(0.0, 1.0, square1TriColor.value, COLORS.yellow);
        cAlpha.value = animate(0.0, 1.0, cAlpha.value, 0.0);
        let p = square1Origin.add(vec2((a+b)&#x2F;2, (a+b)&#x2F;2));
        cmd(new Save());
        cmd(new Translate(p));
        cmd(new Scale1(scale));
        cmd(new SetAlpha(alpha));
        cmd(new Label(vec2(0, 0), &quot;c²&quot;, color, FONT));
        cmd(new Restore());
    }
    currentSection += 1;

    &#x2F;&#x2F; Draw the second square.
    let square2Origin = vec2(220, 170);
    let square2Rotation1 = ref(0);
    let square2Rotation2 = ref(0);
    {
        let p = animate(0.0, 1.0, square1Origin, square2Origin);
        let alpha = animate(0.0, 1.0, 0.0, 1.0);
        cmd(new Save());
        cmd(new Translate(p));
        cmd(new SetAlpha(alpha));
        cmd(new Polygon(squarePoints, COLORS.blue, null, null));
        &#x2F;&#x2F; Draw the 2 non-animated triangles.
        for (let i = 0; i &lt; 2; ++i) {
            cmd(new Save());
            cmd(new Translate(triOrigins[i]));
            cmd(new Rotate(triRotations[i]));
            cmd(new Polygon(triPoints, COLORS.yellow, null, null));
            cmd(new Restore());
        }
        &#x2F;&#x2F; Draw animated triangles.
        {
            cmd(new Save());
            cmd(new Translate(vec2(b, 0)));
            cmd(new Rotate(square2Rotation1));
            cmd(new Translate(vec2(a, 0)));
            cmd(new Rotate(-180));
            cmd(new Polygon(triPoints, COLORS.yellow, null, null));
            cmd(new Restore());
        }
        {
            cmd(new Save());
            cmd(new Translate(vec2(a, a+b)));
            cmd(new Rotate(square2Rotation2));
            cmd(new Translate(vec2(b, 0)));
            cmd(new Rotate(-90));
            cmd(new Polygon(triPoints, COLORS.yellow, null, null));
            cmd(new Restore());
        }
        cmd(new Restore());
    }
    currentSection += 1;

    &#x2F;&#x2F; Rotate two triangles in the second square.
    {
        square2Rotation1.value = animate(0.0, 0.5, square2Rotation1.value, 90);
        square2Rotation2.value = animate(0.5, 1.0, square2Rotation2.value, -90);
    }
    currentSection += 1;

    &#x2F;&#x2F; Label the sides of &quot;a&quot; and &quot;b&quot; squares.
    let aAlpha = ref(0.0);
    let bAlpha = ref(0.0);
    {
        let squareA = [
            vec2(b, 0), vec2(a+b, 0),
            vec2(a+b, a), vec2(b, a),
        ];
        let squareB = [
            vec2(a, a), vec2(a+b, a),
            vec2(a+b, a+b), vec2(a, a+b),
        ];
        let lineAlpha = animate(0.0, 0.2, 0.0, 0.5);
        aAlpha.value = animate(0.0, 0.5, aAlpha.value, 1.0);
        let aScale = animate(0.0, 0.5, 20, 1);
        let aColor = animate(0.0, 0.5, COLORS.blue, COLORS.bg);
        bAlpha.value = animate(0.65, 1.0, bAlpha.value, 1.0);
        let bScale = animate(0.65, 1.0, 20, 0.6);
        let bColor = animate(0.65, 1.0, COLORS.blue, COLORS.bg);
        cmd(new Save());
        cmd(new Translate(square2Origin));
        {
            cmd(new Save());
            cmd(new SetAlpha(lineAlpha));
            cmd(new Line(vec2(0, a), vec2(a+b, a), 1, COLORS.yellow));
            cmd(new Restore());
        }
        for (let i = 0; i &lt; squareA.length; ++i) {
            let p1 = squareA[i];
            let p2 = squareA[(i + 1) % squareA.length];
            let p = pointAlongLine(p1, p2, 0.5, 10);
            cmd(new Save());
            cmd(new SetAlpha(aAlpha));
            cmd(new Translate(p));
            cmd(new Scale1(aScale));
            cmd(new Label(vec2(0, 0), &quot;a&quot;, aColor, FONT));
            cmd(new Restore());
        }
        for (let i = 0; i &lt; squareB.length; ++i) {
            let p1 = squareB[i];
            let p2 = squareB[(i + 1) % squareA.length];
            let p = pointAlongLine(p1, p2, 0.5, 7);
            cmd(new Save());
            cmd(new SetAlpha(bAlpha));
            cmd(new Translate(p));
            cmd(new Scale1(bScale));
            cmd(new Label(vec2(0, 0), &quot;b&quot;, bColor, FONT));
            cmd(new Restore());
        }
        cmd(new Restore());
    }
    currentSection += 1;

    &#x2F;&#x2F; Label the areas of &quot;a&quot; and &quot;b&quot; squares.
    {
        aAlpha.value = animate(0.0, 1.0, aAlpha.value, 0.0);
        bAlpha.value = animate(0.0, 1.0, bAlpha.value, 0.0);
        let a2Origin = vec2(b, 0).add(vec2(a+b, a)).scale(0.5);
        let b2Origin = vec2(a, a).add(vec2(a+b, a+b)).scale(0.5);
        let alpha = animate(0.0, 1.0, 0.0, 1.0);
        let scale = animate(0.0, 1.0, 30, 1.0);
        let color = animate(0.0, 1.0, COLORS.blue, COLORS.bg);
        cmd(new Save());
        cmd(new SetAlpha(alpha));
        cmd(new Translate(square2Origin));
        {
            cmd(new Save());
            cmd(new Translate(a2Origin));
            cmd(new Scale1(scale));
            cmd(new Label(vec2(0, 0), &quot;a²&quot;, color, FONT));
            cmd(new Restore());
        }
        {
            cmd(new Save());
            cmd(new Translate(b2Origin));
            cmd(new Scale1(scale));
            cmd(new Label(vec2(0, 0), &quot;b²&quot;, color, FONT));
            cmd(new Restore());
        }
        cmd(new Restore());
    }
    currentSection += 1;

    &#x2F;&#x2F; Execute draw commands.
    let ctx = target.context;
    ctx.save();
    {
        let f = target.scale * target.pixelRatio;
        ctx.scale(f, f);
    }
    for (let obj of commands) {
        obj.execute(ctx);
    }
    ctx.restore();
}


&#x2F;&#x2F;
&#x2F;&#x2F; updateLoop
&#x2F;&#x2F; initialize
&#x2F;&#x2F;

function updateLoop(froll) {
    froll.update();
    requestAnimationFrame(function () { updateLoop(froll); });
}

function initialize() {
    let root = document.getElementById(&quot;filmroll-1&quot;);
    let froll = new Filmroll(root, drawPythagoras);
    updateLoop(froll);
}

document.addEventListener(&quot;DOMContentLoaded&quot;, initialize);
&lt;&#x2F;script&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Beware of Sync Platforms</title>
        <published>2013-07-12T17:52:00+00:00</published>
        <updated>2013-07-12T17:52:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kssreeram.org/blog/beware-of-sync-platforms/"/>
        <id>https://kssreeram.org/blog/beware-of-sync-platforms/</id>
        
        <content type="html" xml:base="https://kssreeram.org/blog/beware-of-sync-platforms/">&lt;p&gt;Recently, Dropbox &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.dropbox.com&#x2F;developers&#x2F;blog&#x2F;43&#x2F;the-datastore-api-a-new-way-to-store-and-sync-app-data%3E&quot;&gt;announced&lt;&#x2F;a&gt; their database sync platform.&lt;&#x2F;p&gt;
&lt;p&gt;At first sight, it looked simple and clean. But I was disappointed
when I saw this:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Forget conflicts and combining changes when people use your app from different devices. With datastores, Dropbox can now understand the structure of your app data and automatically merge changes made at the same time. For example, simultaneous edits to a contact&#x27;s phone number and email address will be merged without sync conflicts or any action from the user.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Any sync-platform that claims to perform automatic conflict resolution
is broken. This is because conflict resolution is fundamentally a
domain specific problem that cannot be magically solved by a generic
platform.&lt;&#x2F;p&gt;
&lt;p&gt;Let me explain with an example.&lt;&#x2F;p&gt;
&lt;p&gt;Dropbox does not consider changes to two different fields of the same
record as conflicting. That&#x27;s a problem.&lt;&#x2F;p&gt;
&lt;p&gt;To see why, consider the following scenario:&lt;&#x2F;p&gt;
&lt;p&gt;My wife and I are going on a picnic with friends. We use a task-list
app with two fields per task - &quot;task name&quot; (a string) and &quot;completed&quot;
(a boolean) - to create a shopping list that&#x27;s shared between the two
of us.&lt;sup id=&quot;fnr1-20130712&quot;&gt;[&lt;a href=&quot;https:&#x2F;&#x2F;kssreeram.org&#x2F;blog&#x2F;beware-of-sync-platforms&#x2F;#fn1-20130712&quot;&gt;1&lt;&#x2F;a&gt;]&lt;a href=&quot;https:&#x2F;&#x2F;kssreeram.org&#x2F;blog&#x2F;beware-of-sync-platforms&#x2F;#fn1-20130712&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The list contains the following items that are yet to be completed.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;12 AAA batteries&lt;&#x2F;li&gt;
&lt;li&gt;4 foldable chairs&lt;&#x2F;li&gt;
&lt;li&gt;bread and butter&lt;&#x2F;li&gt;
&lt;li&gt;dry wood for fire&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Once an item is completed, it is moved off the list and displayed in a
separate section.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s say my wife realizes we actually need 5 chairs, not 4. She goes
ahead and changes &quot;4 foldable chairs&quot; to &quot;5 foldable chairs&quot;. She
happens to be offline when she makes this change, and hence this
change isn&#x27;t visible to the server immediately.&lt;&#x2F;p&gt;
&lt;p&gt;At around the same time, I buy the 4 chairs. I go to the app and look
for the item &quot;4 foldable chairs&quot; and mark it as completed. The item is
moved off my list into the completed section.&lt;&#x2F;p&gt;
&lt;p&gt;Once my wife gets online, the app pushes her changes to the server. My
wife&#x27;s change modifies the &quot;task name&quot; field. My change modifies the
&quot;completed&quot; field. If Dropbox were used for syncing, since the two
fields are independent, it would automatically apply both
changes. Dropbox sees that there are two simultaneous changes to the
same record, but it does not consider this to be a conflict because
the two changes operate on two different fields.&lt;&#x2F;p&gt;
&lt;p&gt;This effectively results in the item &quot;5 foldable chairs&quot; being marked
as completed.&lt;&#x2F;p&gt;
&lt;p&gt;In the context of this task-list app, Dropbox&#x27;s automatic merging has
resulted in a situation that&#x27;s akin to losing data! We did not buy 5
chairs, but the app makes us believe that we are done buying
chairs. That&#x27;s just as bad as losing data.&lt;&#x2F;p&gt;
&lt;p&gt;In essense, the notion of a &lt;em&gt;conflict&lt;&#x2F;em&gt; depends on the &lt;em&gt;meaning&lt;&#x2F;em&gt; that
we assign to the data. Dropbox knows nothing about the meaning. It
only knows the shape of the data. And that&#x27;s not enough to resolve
conflicts.&lt;&#x2F;p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
	&lt;hr&#x2F;&gt;
	&lt;ol&gt;
		&lt;li id=&quot;fn1-20130712&quot;&gt;
			&lt;p&gt;Dropbox doesn&#x27;t yet allow two different accounts to share the same datastore. For now, assume that my wife and I share a common dropbox account. &lt;a href=&quot;#fnr1-20130712&quot;&gt;&amp;#8617;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
		&lt;&#x2F;li&gt;
	&lt;&#x2F;ol&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>What made C great</title>
        <published>2011-10-13T13:19:00+00:00</published>
        <updated>2011-10-13T13:19:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://kssreeram.org/blog/what-made-c-great/"/>
        <id>https://kssreeram.org/blog/what-made-c-great/</id>
        
        <content type="html" xml:base="https://kssreeram.org/blog/what-made-c-great/">&lt;p&gt;The man himself &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;cm.bell-labs.com&#x2F;cm&#x2F;cs&#x2F;who&#x2F;dmr&#x2F;chist.html&quot;&gt;said&lt;&#x2F;a&gt; it best:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;C is quirky, flawed, and an enormous success. While accidents of history surely helped, it evidently satisfied a need for a system implementation language efficient enough to displace assembly language, yet sufficiently abstract and fluent to describe algorithms and interactions in a wide variety of environments.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Thank you, Dennis Ritchie, for giving us C.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
