はじめに
emadurandalと申します。
以前「GLBoost」というWebGLライブラリを開発していました。昨年も記事を書いていたのでご記憶の方もいらっしゃるかと思います。
現在はより新しいライブラリを開発中です。本来はこの記事と同時にOSSとして公開予定でしたが、準備が間に合わなかったため、それまでに蓄えた知見の共有のみにとどめます。
この記事では、(主にWebGLなどの)ライブラリを開発する際に有用になるであろう知見や経験論などをまとめていきます。
なぜ作りたいのかを自問しよう
いきなり精神論から入ってしまいますが、お許しください。
ライブラリ開発を始めるのは簡単ですが、続けることが難しいものです。自問自答し続けましょう。
ライブラリ開発で自分は何を得たいのか
- 例1:概念だけの知識を血肉化したい
- 例2:自分の技術力を世に問いたい
- 例3:達成したい目標を実現するのに、既製品では不足がある
- 例4:既製品でも実現できるが、目標を達成するのに、ライブラリ・ツールも含めてすべてを自作したい
- 例5:プロダクトオーナーとして一目置かれたい
私の場合は1と4でした。どれが正解とはいいません。自分にとって強いモチベーションの理由になるなら、それがあなたの正解です。OSS開発は上手く続けば、2年、3年……それ以上と、人生の内無視できないほどの長大な時間を投入します。あなたの動機は、それだけの時間を投じるに足るものでしょうか?
個人的な経験ですが、無理に高尚なビジョンを掲げるよりも、割と俗っぽい自分の自然な欲求をモチベーションにした方が、しぶとく長続きしそうにも思います。
作り始める、その前に
その分野のライブラリを、今の自分は作って公開して良い立場なのか確認しよう
以前から不思議に思っていたことがあります。自分よりもずっと優秀なCGエンジニアの人々が沢山いらっしゃるわけですが、なぜ彼らはライブラリを自分で開発して、公開するケースが少ないのか。よっぽど優秀なんだから、作ればそれだけすごいプロダクトができて、きっと大きな支持を集められるだろうに……と。
最近になってようやくわかりましたが、おそらく作りたくないのではありません。多くの場合、作れないのです。
結論から言うと、勤め先の会社から禁止されてしまうケースです。勤めている会社が主戦場としている分野と同じ分野のライブラリを開発し、公開することは、自分が競合になりえてしまいますし、なにより、会社の技術資産の流用の疑いを問われる危険性があります。
そのため、エンジニアや研究者は、ケースによっては就職時・転職時に、会社との雇用契約で、会社と同じ分野の技術プロダクトをOSS公開しない、またはすでにしていた場合は公開を取りやめたり、更新を停止させることに同意しなければならない場合があります。
仮に、明確に禁止されていなかったとしても、そうした猜疑が将来的に発生しかねない分野でのライブラリ開発・公開はトラブルを避けるため、控えておこうという判断を、皆さんされていたのではないかと推察します。
ですので、もしあなたが自分の得意分野でライブラリ開発をして、それをOSS公開してみよう、と思った場合、念の為その点を会社に相談して、必ず許諾を得るようにしましょう。これは、自分の身を守ることはもちろん、お世話になっている会社に迷惑をかけないためでもあります。
ちなみに私の場合は、同じCGという技術分野であるにも関わらず、会社がライブラリのOSS開発について理解を示してくれているため、その点はとても会社に感謝しています。
開発・運用方針
OSSとしてパブリックにする前からnpm運用しよう
みなさんもnpmまたはyarnコマンドで、JavaScriptライブラリのパッケージをインストールしたことがあると思います。ようは自分のライブラリをGithubで公開するだけでなく、npmコマンドでインストールすることもできる形態にしよう、ということです。
これは開発の途中からではなく、最初期の段階から準備することをおすすめします。
初期からnpm運用していないと、ライブラリの設計がnpmのモジュールとして機能しない欠陥を抱えたままコード規模が膨れていく危険性があるからです。OSS公開の手前で慌ててモジュール対応しようとして、設計変更で大変苦労する、という失敗を踏みかねません。
初期から対応することの副次的効果として、npm運用することで、自然とセマンティック・バージョニングを行わざるを得ない状況になる、というメリットもあります。npm publishの実行にあたっては、必ずpackage.jsonのversionプロパティのバージョン数値を更新する必要がある(そうしないとpublishが失敗する)ためです。
とはいえ、まだまだ開発の途上にある段階では、ライブラリをおいそれとnpmでパブリック公開するわけにはいきません。
おすすめは、プライベートな独自のnpmサーバーを構築して運用することです。
私の場合は、「Verdaccio」というプライベートnpmサーバーのDocker版を、自宅にあるSynology社製のNASのDockerサーバー機能でインターネット公開してプライベート運用しています。少し設定が必要ですが、アクセスするユーザーを制限することも可能です。
セマンティック・バージョニングを導入しよう
セマンティック・バージョニングについてはこちらを御覧ください。三桁のバージョン位置のどこが変わったかで、その影響を明確にすることができます。
バージョンを以下で表すとして、
x.y.z
xが変わった場合は、それまでの互換性を崩す変更が加わったことを意味します。
yが変わった場合は、機能が追加されたことを意味します。
zが変わった場合は、機能の追加はなく、バグ修正にとどまることを意味します。
つまり、メジャーバージョンが変わらない限りは、依存しているソフトウェアはそのまま動く(べき)ことを意味します。
バージョンの更新方法
バージョンを記録・更新すべき場所は最低でもpackage.jsonとGitタグの二箇所あります。この二箇所を手動でそれぞれ更新するのは避けましょう。間違いのもとです。
npmでパッケージ管理しているのであれば、以下のいずれかのコマンドで、これら二箇所を同時にバージョンインクリメントすることができます。
$ npm version major
$ npm version minor
$ npm version patch
コミットコメントに規約を取り入れよう
セマンティックバージョニングについてご存じの方は多いと思いますが、Conventional Commitsについてご存知でしょうか。現在はコミットコメントの書き方についてもベストプラクティスがあります。
https://www.conventionalcommits.org/ja/v1.0.0-beta.4/
これの規約に従うことのメリットの一つは、関連サポートツールによって、新バージョンリリース時のチェンジログを自動生成できることです。
TypeScriptを採用しよう
JavaScript系のライブラリの場合、よほどの理由がない場合は今や必ず導入すべきです。そういう時勢になっていますので、「そうなの?」と思った方はWebで調べてみてください。ここではいちいち説明しません。
テストを書こう
テストを導入すべき理由を挙げます。
テストなしで、手動で挙動確認する運用では絶対に漏れが発生する
GLBoost時代はテストが実質なく、いくつかあるサンプルを手動で動作チェックしていました。人は面倒くさがりなので、確認の省略が頻繁に発生し、実際の運用でエラーが発覚して直すという最悪のやり方でした。
- 手動確認では漏れや怠りが発生するため、エラーを頻繁に見過ごす。
- 不具合の発見自体が遅れるため、コード履歴を深くたどることになり修正コストが大きくなる。
- 本運用でエラーに見舞われるため、ステークホルダーに迷惑をかける
三拍子そろったダメ運用ですので、テスト無しは絶対にやめてください。また、テスト運用していたとしても、カバレッジ率が低いと似たような状況を招きますので、テストの整備は怠らないようにしましょう。
テストがあれば、リファクタリングなどのコード変更を自信を持って行える。テスト通過により、コード変更後も仕様を満たすことを確認できる。
GLBoostライブラリを捨てる決断をしたのは、主にこれが理由です。テストコードがなかったため、思い切ったリファクタリングや設計改善が思うようにできず(GLBoostは商用利用もされていたので、なおさらでした)、大半がまさにレガシーコード(技術負債)と化してしまっていたのです。
**テストコードの不足は、そのプロダクトの寿命を確実に縮めると断言できます。**気をつけてください。
現在開発中の次世代ライブラリでは、その轍を踏まないようにしようとしています。
テストコードはそのプロダクトのコード手本、または仕様書の代替になりうる
各テスト項目は、各機能を手短に呼び出すコードの手続きによって成り立っています。つまり、テストコードを見ればそのプロダクトの使い方がわかるということです。
テストコードが無いプロダクトは信用されない
少なくとも、技術者サイドから見ればそう見る人が多いでしょう。プロダクトへの信頼を勝ち取るためにも、テストを用意しましょう。
E2Eテストで描画結果もチェックしよう
WebGLライブラリなど、描画を伴うライブラリでは、E2Eテストの導入を強く勧めます。これは、テスト実行に描画結果のキャプチャー画像を取得して、正解画像と比較するというテストです。
これは、細かい個々の機能の確認を棚上げにして、全体動作を通して何かしら不具合がないかチェックしますので、個々のテストでも網羅しきれない(または描画という性質上、ユニットテストではそもそも確認できない)動作を一発でチェックできます。
具体的な導入については、こちらの記事が参考になるでしょう。
「JestとPuppeteerでお手軽(Visual)レグレッションテスト」 - Qiita
WebGLライブラリを開発しようとされている方は、このE2Eテストは絶対に導入がマストです。というのも、不具合の大半はユニットテストではなく、このE2Eテストで見つかるからです。
ゲーム会社などでは、ゲームエンジンのリグレッションテストで、過去の(QA・テスト済みの)タイトル作を動作させてみて、キャプチャベースで不具合をざっと洗い出すといったことが行われているようです。
主要なテキストエディタのフォルダ設定を決め打ちしてリポジトリに含めよう。
これについては、賛否もあると思いますので強制ではありません。
エディタの設定について、vim化したいかとかフォントサイズとか、そこらへんは各自の自由にさせるべきですが、あるべきインデント数やリンターの設定などは、コードの品質に関わるので統一することが必要です。
これらの設定は実際には自分の使用するエディターを通して私達の開発効率に関わってきますので、主要なエディタ(またはVSCodeなど推奨エディタを1つに決め打ってもかまわないでしょう)について、「フォルダ設定」(VSCodeなら.vscode)としてインデント数やリンター設定など、コード品質に関わる重要な設定を施し、その設定ファイルをGitリポジトリに含めてしまいます。
こうすることで、全てのコントリビューターでコード品質に関わる望ましい設定を強制させることができます。
設定の際は、ワークスペース設定やエディタ全体の設定など、優先度の弱いレイヤーの設定値が悪さしないように、上書きされたくない設定値についてもフォルダ設定の方で望ましい設定値を書き込んでおき、設定上書きさせないようにすると良いでしょう。
(もちろんフォントサイズやエディタのテーマカラーなどについては、各位の自由にさせてあげるために、それらは決め打たないであげておきましょう)。
実際に、Babylon.jsなどのプロジェクトでは、.vscodeフォルダをリポジトリに含めているようです。
実装ノウハウ
パフォーマンスを重視するなら、非同期処理を前提としよう
近年のパフォーマンス重視のライブラリは、その多くが非同期処理を前提としています。物によっては同期処理型のメソッドの存在を禁止しているものすらあるほどです。
以下のコードをまず見てください。
// main.js
function asyncFn() {
return new Promise((resolve)=>{
setTimeout(()=>{
resolve('end!');
}, 3000);
});
}
async function awaitFn() {
return await asyncFn();
}
async function main() {
console.log('start!');
const str = await awaitFn(); // (1) こちらは3秒待機する(同期処理)
// const str = awaitFn(); // (2) こちらはすぐに戻ってくる(非同期処理)
console.log(str);
}
main();
Node.jsをインストールされている方は、以下を実行することで試すことができます。もちろんブラウザでも試せます。
$ node main.js
(1)と(2)の行をコメントアウトで切り替えて、それぞれ動作を比べてみてください。(1)は3秒待機しますが、(2)はすぐに制御が戻りますね。
つまり、ayncがついている関数であれば、その関数の中身でawaitを使ってどれだけ同期待ちをしていたとしても、関数呼び出し自体はすぐに制御が返ってくるということです。
async/awaitの仕様を思い出してください。asyncをつけた関数でないと関数内でawaitを使えないのでしたね(ECMAScriptの最新ドラフト仕様では状況が変化しているようですが)。このような言語仕様になっている理由としては、awaitを使いすぎて同期待ちによりJavaScript全体の処理が遅くならないように、という考慮でしょう。呼び出す関数にasyncがついているなら、その呼出し側でawaitを使わなければ、必ずすぐに制御が戻ってくれます。
awaitの使用は慎重に
では、そういう言語仕様だからと、安心してawaitを使いまくってよいのでしょうか? そんなわけはありません。
関数内部でawaitをむやみに使うと、その関数の処理がresolve(async関数は水面下でPromiseオブジェクトを生成して返すことを思い出してください)するのに、より長い時間がかかることになります。
ほとんどのソフトウェアにおいて、処理の都合上、非同期処理をどこかで待機しなければならない局面は必ずあります。問題はその待機する箇所での待ち時間です。resolveされるのが遅ければ、そこの待機箇所では制御が戻るのが確実に遅れます。その結果、ソフトウェアの応答速度の遅延に繋がるわけです。
そうした事態をさけるために、外部リソースへのアクセスなどを行う関数の内部では、awaitの使用を極力抑えるべきです。awaitを使って同期待ちをしてよいのは、その処理が完了しないと他の処理を始められない場合のみと考えてください(awaitを使うことで可読性が向上するメリットはあるのですが、この記事ではパフォーマンスを重視して設計・実装を行うという文脈です)。並列実行できる箇所では、PromiseやPromise.allをできるだけ活用しましょう。
「制約をかけること」は大事
ユーザーに自由度を与えることは、実際求められることですし、大切なことなのですが、何でもかんでも応じてはいけません。
設計上、「制約をうまくかける」ことで、得られることは非常に多くあります。
- 予め決めた制約に従ってもらうことで、処理の並列化などのパフォーマンス向上を意識した設計を行いやすい。
例えば、処理の単位を独自の「タスク」クラスに所定の作法で実装することをユーザープログラマに強制することで、マルチコアCPUの並列化を活用した処理の仕組みに投げ込み、高いパフォーマンスを実現することができるかもしれません。
- APIの使い方が、制約により分かりやすくなる
例えば、そのクラスが依存する外部インスタンスを、そのクラスのコンストラクタで最初に全て渡さないと実体化できないようにすれば、APIの呼び出し順序をうまく縛ることができ、分かりやすくなります。
class Mesh {
private _gl: WebGLRenderingContext;
constructor(gl: WebGLRenderingContext) {
this._gl = gl;
}
}
これを、例えば、依存する外部インスタンスを、インスタンスメソッドでいつでも設定できる、というようにしてしまうと、呼び出し順序があまりに自由になりすぎてしまい、ユーザーサイドにとっては、どの呼び出し順序ならライブラリとして正しく動作するのか非常にわかりづらくなります。
実際、コンストラクタで渡すようにしなければ、クラス内部では外部インスタンスを保持するメンバ変数が未初期化である期間が発生してしまい、不具合の温床にもなりますよね。
class Mesh {
private _gl?: WebGLRenderingContext;
constructor() {
}
setWebGLContext(gl: WebGLRenderingContext) {
this._gl = gl;
}
}
この「どういう順序でAPIの呼び出しをするのが正しいのかわからない」問題は、些細なようでいて、思いの外トラブルを招く重大な問題になりえます。
これは、私が(自分が設計したライブラリではありませんでしたが)ある案件に参画した際にクライアントからの強いクレームで実際に学んだことです。
前任者のライブラリの開発を引き継いだ案件だったのですが、API体系を多少修正はしましたが、すでにかなりの実装規模になっており、根本的なAPI刷新は難しい状況でした。
わざわざ「API呼び出し順序表」をエクセルで作成してクライアントに提出し理解を求めるなど、解決にまでかなり時間がかかりました。
それまでの間、クライアントのエンジニアには、「正しいAPIの呼び出し方」にたどり着くまで、ライブラリが期待通りの動作をしてくれず、エラーにずっと悩まされるというストレスを強いてしまいました。解決するまで、そのライブラリを使用した製品開発に多大な影響が出ていたのです。
これも、設計当初からきちんとAPI的に制約を注意深く設けていれば、ユーザープログラマがコーディングを行う時点で、迷うような余地をそもそも無くすることができていたはずです。
ユースケースでは自由を、コード設計には制約を
とはいえ、一方でユーザーは「あれもやりたい、これもやりたい」と自由を求めてもいます。どう折り合いをつければよいのでしょうか。
一つのポイントとしては、ライブラリのユースケース面では自由さを(やりすぎない範囲で)考慮し、 コード設計面では制約の方をより重視する、という方針にすると良い方向へ向かうケースが経験上多い気がします。
また、一度自分で必要だと決めた制約方針については、たとえユーザーから「例外を認めてほしい」と要望されても、やすやすと応じてはいけません。その制約こそが、あなたのライブラリに秩序とメリットをもたらしているからです。ライブラリの設計というフィールドにおいて、ユーザーは神様ではありません。自分こそが神です。安請け合いして、せっかく作った秩序を自ら壊さないようにしましょう。
コメントは英語で
コードのコメントも、Gitのコメントもそうですが、英語にしましょう。現実問題として日本語などの母国語を使ってしまうと、海外勢からは選択肢として最初から外されてしまう危険性が高まります。
そういう意味では、GithubのIssueやPullrequestなども、英語で運用した方が良いでしょう。日本語などでのサポートを行いたい場合は、別途フォーラムを用意するなどで対応するとよいでしょう。
とはいえ、たとえ注意喚起していても、日本人の多くはIssueを日本語で書いてしまう傾向にあるので、なかなか運用が難しいところではあるのですが。
決めた指針は全体に渡って一貫させよう
コード実装にあたって、決めるべき判断は山ほどあります。明らかに何かしら決めた方がよい以下のような規約や、
- コーディング規約
- API上の規約
ものによっては、プログラマによって賛否両論で結論をつけるのが難しい判断もあることでしょう。
- ソースコードコメントとして、処理の説明コメントは書くべきか書かないべきか
大事なのは、いずれにせよ決めることは決めて、それをライブラリの隅々に渡って一貫させることです。
私の場合は、GLBoostというライブラリでその一貫性を怠り、コード全体としてかなりちぐはぐな様相をもたらしてしまった苦い失敗経験があります。様々な規約や方針をあれもこれも、ライブラリの異なる箇所で試してしまったのです。
一貫性のない、迷いのあるコード体系は、利用者を不安にさせます。その上、何より不便です。
きちんとしたプロダクトを世に出したいなら、自分のライブラリのソースコードを実験場にしてはいけません。「これはリリースされて様々な人の目に触れるコードなんだ!」とゆめゆめ自戒しましょう。途中で規約や方針を変えるなら、ソースコードの隅々に渡って変更を徹底し、リファクタリングとテストを行うべきです。
機械の力を借りてスペルミスを撲滅しよう
英語のネイティブスピーカーでもない限り、単語のスペルミスはやりがちです(他にも焦ってタイピングしていると)。
VSCodeには「Code Spell Checker」というスペルチェックプラグインがありますので、活用してスペルミスを撲滅しましょう。
これは、OSSのコードとして恥ずかしくないものにするため、という点からも大事ですが、それ以上に一度間違ったスペルでリリースしてしまったコードが公開APIメソッドだったりすると大変だから、という意味での予防策でもあります。
いかにライブラリの存在意義を世に示すか
Web3D界隈においては、Three.jsやBabylonJSなどの現行世界トップクラスのライブラリたちがひしめいています。
ネイティブ系を見渡しても、世の中にはUnityだのUnreal Engineだのが隆盛しているわけです。一流の技術者・研究者を大量に雇用して開発を進めるこれらの強豪に対して、実用性や汎用性で立ち向かうのは流石に無理があります。
生き残るために、差別化と研鑽は重要です。
他のライブラリから勉強する
まず他のライブラリのことを知らなくてはなりません。Three.jsやBabylonJSといった優れたライブラリから学びましょう。設計思想や個々のテクニックについて、多くの知見を得られるでしょう。自分でそれらに匹敵するアイデアを導き出すのに掛かる膨大な時間を考えれば、コードリーディングするだけでそれらを一瞬で得られる利点は計り知れません。
その分野のベストプラクティスを学ぶ
少しだけWebGLというかリアルタイムCGらしい記述も書こうと思います。
その分野のライブラリで、大抵は専門的な処理があると思います。例えばリアルタイムレンダリングライブラリであれば、
- シーングラフの更新処理をいかに効率的に行うか
- 描画の単位をいかに整理するか
といったことです。
シーングラフの更新処理をいかに効率的に行うか
せっかく例に挙げたので少し考えてみましょう。シーングラフにぶら下がっている各ノードのWorld行列を求めるには、シーングラフの上層からノードのLocal行列を積算していく必要がありますが、処理中のノードより上層のworld行列の計算結果は、同じ結果なのに何度も繰り返し計算したくないものです。
この解決方法は「Update Scene graph」などで検索すると、ネット上にたくさんの議論が見つかります。NVIDIAあたりがGPUで解決するための資料なども出しています。
アプローチの一つとしては、「シーングラフ階層をトポロジカルソートしてシーケンシャルにノードを並べる」というものがあります。上層から下層に向かって整列されるため、「自分のlocal行列 ✕ 一つ親のworld行列」という計算をシーケンシャルに実行すれば、無駄な重複計算をせずに計算が完了することになります。
ただし、このアプローチではシーングラフ階層が変更になった場合、トポロジカルソートを完全にやり直すのはコストもかかるため、そこをいかに効率化(部分的な更新で済ませるように工夫するなど)するか、という別の課題も出てくることでしょう。
描画の単位をいかに整理するか
できれば描画処理の多くをインスタンス描画で処理したいものです(WebGLなどは3DAPIとしてはオーバーヘッドが大きいのでなおさらです)。
しかし、半透明な物体などは奥から手前へソートしなければなりませんし、またアルファブレンディングなどの3DAPIとしてのステート変更などを伴う描画物が混じっていたりすると(殆どの場合混じってます!)、すべてをインスタンス描画するわけにもいかず、描画の単位を分けないといけません。
そのため、マテリアル設定が同じ描画物を、できるだけ同じタイミングで描画するようにしたり、半透明な描画物は不透明の描画物をすべて描画しきった後で描画するように徹底したりしないといけないのですが、そうした描画物の描画のタイミングや順序をどうスマートに制御すべきか、というのは常に悩みどころです。
こうしたエンジン実装寄りのテーマは非常に地味なので、3D関連のプログラミング入門本で取り扱われるケースは非常にまれでしょう。自分から進んで調査しなければなりません。
かくいう私の場合は、自分が主催しているCG開発コミュニティで質問してある方から教えていただいた(進んで調査してない笑)のですが、クレバーなアプローチがやはり、コンソールゲーム業界にはあったりします。
Order your graphics draw calls around! - realtimecollisiondetection.net
こちらの方のブログで紹介されているアプローチでは、マテリアルIDや半透明情報などの「描画タイミングにおける複数の尺度」について優先度を決め、優先度の高い尺度を64bit整数の上位ビットから割り当てていきます。実際の描画物すべてにこの64bit整数をもたせておき、その描画物についてのマテリアルIDや半透明情報を整数にビット操作で書き込みます。
そうすると、描画物をこの整数について単純にソートするだけで、各尺度の優先度を考慮したスマートな描画タイミングの並び替えが完了してしまうのです。
こんな方法、少なくとも私は一人で思いつく自信がありません。自分で変に自己流にこだわっていたら、もっと非効率な実装をライブラリに施してリリースしていたでしょう。世にはこうしたベストプラクティスがたくさんあるため、一人で苦労するより、進んでこうした事例を調べた方が良いですね。
繰り返しになりますが、主流のライブラリのソースコードリーディングからも、こうした知見は得られることでしょう。
周り(ライバル)を気にしすぎない
学ぶと同時に、あまりに他の競合(?)ライブラリの動向に気を取られすぎるのも考えものです。
ライブラリ開発を続けるにあたり大事なことは**「よそはよそ、自分は自分」**という考え方です。
ライブラリとしては競合にかなわない小さな機能セットでも、それ自体できちんと完結しており、扱いやすく、コンセプトがはっきりしていれば、利用してくれる可能性は十分あります。
機能面で追いつくことばかりを焦り、扱いづらい仕様になってしまったり、バグが多かったり、ドキュメントもテストも不足している状態でプロダクトを公開したり、運営しつづければ、おそらくプロダクトとして失敗するでしょう。
商業のプロダクトやサービスも、そうした事例はたまに見られるような気がします。「他社との機能比較」は非常に気になってしまう要素ではありますが、それ以上に大事なことは、自身のプロダクトのミッション(果たすべき役割)であることを忘れてはいけません。
コンセプトを明確にし、そのコンセプトにおいては優位性を確保する
例えば例ですが、hyperappと呼ばれるWebフレームワークがあります。なんとライブラリサイズが1~2KBしかない超軽量ライブラリであるにも関わらず実用的いうことで、ReactやVueを主流としてきたWebエンジニアの界隈で静かな衝撃が走りました。
WebGLライブラリで超軽量、導入も簡単で手軽にさっと使える、なんてコンセプトは、もしかしたら受けるかもしれませんね。機能追加競争に明け暮れて結局中途半端に終わるより、勝ち目が高いかもしれません。
あるいは、教育用など一部の分野に特化してしまうという手もあります。私のspinel.jsなどはそうですが、実用領域で戦うのではなく、教育用途でその役割を果たすというのだって、立派なライブラリとしての意義があると思います。
ちょうどこれを書いている時に、UnityのTinyプロジェクト(UnityのHTML5向け超軽量ランタイム)が3D対応したという情報が入ってきました。
We just released an update on Unity's #ProjectTiny, with the ability to render 3D graphics in a tiny, all-DOTS executable which runs in mobile browsers with ease.
— Ciro Continisio ☕️ (@CiroContns) 2019年12月12日
All the details here, by @jccim:https://t.co/mhBDGtb2RU pic.twitter.com/N0cytzOMV2
Unityの豊富な機能やエディター編集が使えて、ロードも超軽量です(上記ツイートのレス中のリンクから実際に遊べます)。今までブラウザ環境ということでネイティブ勢の侵攻から守られてきた平和な世界で隆盛してきた既存WebGLライブラリ勢を皆殺しにしかねない程のインパクトです。
今後、WebGLライブラリ勢は生き残りのため必死でしょうね(自分含め)。ますますコンセプトをはっきりさせることが問われるかもしれません。
最後に
日本ではOSSのプロダクト・オーナーが少ないです。
海外ではそのOSSプロダクトを作った本人がコミュニティのトップを張っているのに、日本ではある程度精通はしていれど、1ユーザーに過ぎないエバンジェリストが国内コミュニティのトップとして幅を効かせていることが多いです(それ自体がダメというわけではもちろんないですが)。
私自身、まだ世界に通用するどころか国内市場で広く使われるようなプロダクトすらまだ生み出せていない現状ですが、挑戦者はもっと出てほしいと思います。みんなで挑戦しましょう!