ベクトルの基礎を少し学んで、反射ベクトルの求め方がある程度わかった気がしますので、学習ノートを書いておきます。

円と円が衝突したときの速度ベクトル(入射ベクトル)と反射ベクトル

大きい円は静止していて、それに小さい円が青いベクトル方向にぶつかって来たという想定です。
法線ベクトルは、dx = 小さい円のx座標-大きい円のx座標、dy = 小さい円のy座標-大きい円のy座標で、(dx,dy)という要素のベクトルになります。

反射ベクトルを求めるひし形の平行四辺形

上の図のようにひし形の平行四辺形に対角線を入れた図で考えると、反射ベクトルORは、ベクトルOQとベクトルQRの和というベクトルの基礎を学びました。
ベクトルQR=ベクトルPQ、つまり入射ベクトルと同じ(ベクトルは方向と長さが同じであれば座標位置に関係なく同じ)ですので、ベクトルOQの長さがわかれば(方向は法線ベクトルと同じ)反射ベクトルORが求められるということですよね。

反射ベクトルを求めるひし形の平行四辺形

そして、内角θを挟んで入射ベクトルと長さは同じで逆向きのOP→と法線方向の長さ1のベクトルとの内積で算出されるのは辺OSの長さというのがミソのようです。
ベクトルの基礎で学んだのですが、内積は、2つのベクトルの始点を一致させた場合の内角をθとする計算とのことですので、PO→の逆向きのOP→(-PO→)としないと正しい結果はでないのですね。
でも、実は、入射ベクトルの方向のままのPO→とn→を内積計算した結果は符号が逆になるだけで絶対値は同じになるようです。ですので、PO→のまま内積計算をしてその結果に-1を掛ければ結局OKのようですので、そのようにします。
内積計算の式は、法線ベクトルを仮にn→とすると、
PO→・n→ = |PO→| |n→| cosθ
中点は内積計算の記号で、|で挟んでいるのはベクトルの長さを表しています。内積の結果というのはベクトルではなく、実数なのですね。
さて、ここで|n→|、つまりn→の長さが1だったらどうでしょうか。OP→とn→の内積は、 |OP→| cosθとなります。辺OPの長さ掛けるcosθですね。
これは直角三角形OPSで三角関数から辺OSの長さになります。

n→の長さが1だったらということですので、法線ベクトルを長さが1になるように変換する必要があります。これをnormalizeする、正規化するというそうです。
具体的な計算方法は、法線ベクトルの要素が冒頭で求めた(dx, dy)だとすると、そのベクトルの長さは三角関数で√dxの2乗+dyの2乗なので、dx及びdyをその長さで割った値にすることで長さを1の法線ベクトルを求めることができます。

上の計算方法で自前の正規化関数を記述できますが、p5.jsには正規化関数が用意されています。
let n = createVector(dx, dy).normalize();
これで法線方向の長さ1のn→が出来上がります。

反射ベクトルを求めるひし形の平行四辺形

ということで、速度ベクトル(入射ベクトル)PO→と長さ1にした法線ビクトルn→を内積計算をした結果を符号を逆にすると、OSの長さが求められます。
内積計算もp5.jsに用意されていて、速度ベクトルvと法線ベクトルnの内積は、
p5.Vector.dot(v, n)
で算出され、この結果の符号を逆にしたものが上の図の辺OSの長さということになります。

OSの長さを2倍にするとOQの長さが出ます。そして、
長さ1のn→にOQの長さを掛けるとOQ→というベクトルが得られます。
そうすると、
反射ベクトルOR→ = OQ→ + QR→ = OQ→ + 入射ベクトルPO→
ということで反射ベクトルを求めることができました。

以上から、反射ベクトルをR→、入射ベクトルをV→、正規化した法線ベクトルをN→とすると、
R = -2 ( V ・ N ) N + V  または R = V – 2 ( V・N ) N
と表すことができます。

以上が私の現状の理解です。

実際にp5.jsでコードを書いてみました。こちらのページです。
p5.js 反射ベクトルの勉強

—参考文献—
以下の情報のおかげで大変理解が進みました。

坂田アキラの ベクトルが面白いほどわかる本 (坂田アキラの理系シリーズ)
https://amzn.asia/d/en5Z4ay

C++ 反射ベクトル 「式の解説」Coding Ocean
https://youtu.be/-896y8YXHps?si=TywsoKnc-6IynEGs