[{"data":1,"prerenderedAt":576},["ShallowReactive",2],{"profile":3,"experience":51,"projects":114,"skills":369,"writing":517,"testimonials":575},{"id":4,"title":5,"availability":6,"body":7,"description":13,"email":21,"extension":22,"focus":23,"links":28,"location":41,"meta":42,"name":43,"navigation":44,"path":45,"role":46,"seo":47,"stem":48,"tagline":49,"__hash__":50},"profile\u002Fprofile.md","Profile","Open to AI engineering roles — remote, full-time",{"type":8,"value":9,"toc":17},"minimark",[10,14],[11,12,13],"p",{},"I am an AI engineer who likes the unglamorous half of machine learning: getting a model out of the\nnotebook, putting it behind a real endpoint, watching what it does to real users, and then going\nback to improve it.",[11,15,16],{},"Most of my work sits where ML meets web: image generation for batik patterns, computer vision on\nsatellite imagery, face embeddings for authentication, and the Nuxt\u002FGo\u002FPostgres plumbing that makes\nthose usable. I also write about the engineering side at invasikode.com, because explaining a system\nis a good test of whether I actually understand it.",{"title":18,"searchDepth":19,"depth":19,"links":20},"",2,[],"halo@izzudd.my.id","md",[24,25,26,27],"Model development","Inference & serving","Monitoring & iteration","Shipping the product around it",[29,32,35,38],{"label":30,"href":31},"GitHub","https:\u002F\u002Fgithub.com\u002Fizzudd",{"label":33,"href":34},"LinkedIn","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fdaffa-izzuddin\u002F",{"label":36,"href":37},"Writing","https:\u002F\u002Fwww.invasikode.com",{"label":39,"href":40},"CV","\u002Fcv","Malang, Indonesia · UTC+7",{},"Daffa Izzuddin",true,"\u002Fprofile","AI Engineer",{"description":13},"profile","I take models from a training run to something people can actually use — train, serve, monitor, iterate.","QgL35cJcIc-xNTzcDA2Gmn2dYX3tYL0nwZ-fDZqEQp8",[52,78,95],{"id":53,"title":54,"body":55,"company":54,"companyUrl":59,"current":44,"description":18,"end":59,"extension":22,"highlights":60,"location":61,"meta":62,"navigation":44,"path":63,"period":64,"role":65,"seo":66,"stack":67,"start":74,"stem":75,"summary":18,"type":76,"__hash__":77},"experience\u002Fexperience\u002Frains.md","Rains",{"type":8,"value":56,"toc":57},[],{"title":18,"searchDepth":19,"depth":19,"links":58},[],null,[],"Remote",{},"\u002Fexperience\u002Frains","Present","Tech Lead",{"description":18},[68,69,70,71,72,73],"typescript","vue","nuxt","go","postgresql","docker","2025-01-01","experience\u002Frains","Remote · Full-time","yl-_p3Aw3mxkXzP3CevB8vbEi9ogODBKqF-zjNuiEKY",{"id":79,"title":80,"body":81,"company":80,"companyUrl":59,"current":44,"description":18,"end":59,"extension":22,"highlights":85,"location":61,"meta":86,"navigation":44,"path":87,"period":64,"role":88,"seo":89,"stack":90,"start":91,"stem":92,"summary":18,"type":93,"__hash__":94},"experience\u002Fexperience\u002Fmayfeyr.md","Mayfeyr",{"type":8,"value":82,"toc":83},[],{"title":18,"searchDepth":19,"depth":19,"links":84},[],[],{},"\u002Fexperience\u002Fmayfeyr","Founding Developer",{"description":18},[68,70,72],"2024-06-01","experience\u002Fmayfeyr","Remote · Founding team","plabWmZskFe1CEBvzmvmcoZHeem4_Tc5Wv2Xt1ahwMs",{"id":96,"title":97,"body":98,"company":102,"companyUrl":59,"current":103,"description":18,"end":59,"extension":22,"highlights":104,"location":105,"meta":106,"navigation":44,"path":107,"period":18,"role":108,"seo":109,"stack":110,"start":111,"stem":112,"summary":18,"type":97,"__hash__":113},"experience\u002Fexperience\u002Feducation.md","Education",{"type":8,"value":99,"toc":100},[],{"title":18,"searchDepth":19,"depth":19,"links":101},[],"Universitas Brawijaya",false,[],"Malang, Indonesia",{},"\u002Fexperience\u002Feducation","B.Sc. Computer Science",{"description":18},[],"2019-08-01","experience\u002Feducation","Sr1ut5bmLdEfu9YPya2y0ilAHV4DEhIpTU0W1QzmqJM",[115,191,249,311],{"id":116,"title":117,"body":118,"cover":59,"description":18,"extension":22,"featured":44,"links":167,"meta":169,"metrics":170,"navigation":44,"order":171,"path":172,"pipelineStage":173,"role":174,"seo":175,"stack":176,"status":181,"stem":182,"tagline":183,"tags":184,"timeframe":189,"__hash__":190},"projects\u002Fprojects\u002Fgenbatik.md","GenBatik",{"type":8,"value":119,"toc":162},[120,125,128,132,135,158],[121,122,124],"h2",{"id":123},"the-problem","The problem",[11,126,127],{},"Batik motifs are usually recreated by hand or pulled from a fixed catalogue. GenBatik lets someone\ndescribe the pattern they want in plain language and get a batik-style image back, so a small\nworkshop can explore variants without a designer.",[121,129,131],{"id":130},"what-i-built","What I built",[11,133,134],{},"The whole path: the generation pipeline, the API that queues and streams results, and the interface\nthat shows progress to the user instead of a spinner that lies.",[136,137,138,146,152],"ul",{},[139,140,141,145],"li",{},[142,143,144],"strong",{},"Model",": fine-tuned diffusion on batik imagery, served behind a Python worker.",[139,147,148,151],{},[142,149,150],{},"Serving",": asynchronous job queue with WebSocket progress — generation takes seconds, so the UI\nreports stages instead of blocking a request.",[139,153,154,157],{},[142,155,156],{},"Product",": Nuxt frontend with prompt presets, so a first-time user gets a usable result without\nknowing prompt engineering.",[121,159,161],{"id":160},"what-i-would-change-next","What I would change next",{"title":18,"searchDepth":19,"depth":19,"links":163},[164,165,166],{"id":123,"depth":19,"text":124},{"id":130,"depth":19,"text":131},{"id":160,"depth":19,"text":161},{"live":168,"repo":18},"https:\u002F\u002Fbatik.izzudd.my.id",{},[],1,"\u002Fprojects\u002Fgenbatik",[144,150,156],"Solo — model, backend, frontend",{"title":117,"description":18},[177,178,179,70,180],"python","pytorch","huggingface","websocket","Live","projects\u002Fgenbatik","Text-to-image generation for batik patterns — a diffusion model wrapped in a real product.",[185,186,187,188],"AI","Image Generation","Diffusion","Fullstack","TODO tahun","vuy4-Obqu0HnR3R72z6nIj_dQFYdJsboXso4ch_yN1A",{"id":192,"title":193,"body":194,"cover":59,"description":18,"extension":22,"featured":44,"links":232,"meta":233,"metrics":234,"navigation":44,"order":19,"path":235,"pipelineStage":236,"role":237,"seo":238,"stack":239,"status":59,"stem":242,"tagline":243,"tags":244,"timeframe":189,"__hash__":248},"projects\u002Fprojects\u002Fsparse.md","SPARSE",{"type":8,"value":195,"toc":227},[196,198,201,203,223],[121,197,124],{"id":123},[11,199,200],{},"Mapping rooftop solar panels by hand across a region is slow. SPARSE detects them from satellite\ntiles so the result can be inspected on a map instead of a spreadsheet.",[121,202,131],{"id":130},[136,204,205,211,217],{},[139,206,207,210],{},[142,208,209],{},"Pipeline",": tile ingestion → model inference → geo-referenced output, so each detection keeps\nits coordinates.",[139,212,213,216],{},[142,214,215],{},"API",": FastAPI service that serves predictions per tile.",[139,218,219,222],{},[142,220,221],{},"Interface",": map view where a reviewer can confirm or reject detections.",[121,224,226],{"id":225},"notes","Notes",{"title":18,"searchDepth":19,"depth":19,"links":228},[229,230,231],{"id":123,"depth":19,"text":124},{"id":130,"depth":19,"text":131},{"id":225,"depth":19,"text":226},{},{},[],"\u002Fprojects\u002Fsparse",[144,150,156],"TODO peranmu (model \u002F backend \u002F GIS \u002F semua)",{"title":193,"description":18},[177,178,240,69,241,73],"fastapi","leaflet","projects\u002Fsparse","Solar panel detection on satellite imagery, from model to a GIS interface.",[185,245,246,247],"Computer Vision","Remote Sensing","GIS","qj_RduVHnTq88BVzubu0DFbjidubA8IWRBEgTSygIV8",{"id":250,"title":251,"body":252,"cover":59,"description":18,"extension":22,"featured":44,"links":292,"meta":294,"metrics":295,"navigation":44,"order":296,"path":297,"pipelineStage":298,"role":299,"seo":300,"stack":301,"status":304,"stem":305,"tagline":306,"tags":307,"timeframe":189,"__hash__":310},"projects\u002Fprojects\u002Fidentifeye.md","IdentifEye",{"type":8,"value":253,"toc":287},[254,256,259,263,283],[121,255,124],{"id":123},[11,257,258],{},"Second-factor authentication usually means a code or a hardware key. IdentifEye explores a third\noption: prove identity with a face, without shipping a whole identity platform.",[121,260,262],{"id":261},"how-it-works","How it works",[136,264,265,271,277],{},[139,266,267,270],{},[142,268,269],{},"Embedding",": FaceNet produces a feature vector for each face.",[139,272,273,276],{},[142,274,275],{},"Verification",": similarity score against the enrolled vector, with a threshold instead of a\nbinary classifier — it is a comparison problem, not a classification problem.",[139,278,279,282],{},[142,280,281],{},"Service",": Go HTTP API that wraps the inference step, so the model stays a small independent\nprocess rather than being embedded in the app.",[121,284,286],{"id":285},"what-i-learned","What I learned",{"title":18,"searchDepth":19,"depth":19,"links":288},[289,290,291],{"id":123,"depth":19,"text":124},{"id":261,"depth":19,"text":262},{"id":285,"depth":19,"text":286},{"repo":293},"https:\u002F\u002Fgithub.com\u002Fizzudd\u002FidentifEye",{},[],3,"\u002Fprojects\u002Fidentifeye",[144,150],"Solo",{"title":251,"description":18},[71,177,178,302,303],"react","tailwindcss","Open source","projects\u002Fidentifeye","Face-embedding authentication exposed as a small, boring REST API.",[185,308,309,215],"Vision","Embeddings","KvpSYt6inXR3-ll1CMh2qYzI93GW5j92pl7nPZUc1uE",{"id":312,"title":313,"body":314,"cover":59,"description":18,"extension":22,"featured":103,"links":354,"meta":356,"metrics":357,"navigation":44,"order":358,"path":359,"pipelineStage":360,"role":299,"seo":361,"stack":362,"status":363,"stem":364,"tagline":365,"tags":366,"timeframe":189,"__hash__":368},"projects\u002Fprojects\u002Ftatakata.md","TataKata",{"type":8,"value":315,"toc":349},[316,318,321,325,345],[121,317,124],{"id":123},[11,319,320],{},"Spell-checkers handle typos, not Indonesian grammar. TataKata is an attempt at the harder part:\nnon-standard words, affixes, and punctuation that EYD cares about.",[121,322,324],{"id":323},"approach","Approach",[136,326,327,333,339],{},[139,328,329,332],{},[142,330,331],{},"Rules first",": deterministic EYD\u002FKBBI corrections where the right answer is unambiguous.",[139,334,335,338],{},[142,336,337],{},"Model second",": only send the sentences that rules cannot resolve to a language model, because\nit is slower and less predictable.",[139,340,341,344],{},[142,342,343],{},"Feedback loop",": every accepted correction is stored so the rule set and the model improve.",[121,346,348],{"id":347},"status","Status",{"title":18,"searchDepth":19,"depth":19,"links":350},[351,352,353],{"id":123,"depth":19,"text":124},{"id":323,"depth":19,"text":324},{"id":347,"depth":19,"text":348},{"live":355},"https:\u002F\u002Ftata-kata.netlify.app",{},[],4,"\u002Fprojects\u002Ftatakata",[144],{"title":313,"description":18},[70,72,177],"In progress","projects\u002Ftatakata","An Indonesian text corrector for EYD and KBBI rules — grammar checking for Bahasa Indonesia.",[185,367,36],"NLP","qdN6Y9y8kYvp0b3pDNTQtYzIi9GxAvKDu6351KAS_MU",[370,409,446,480],{"id":371,"title":372,"body":373,"description":18,"extension":22,"items":377,"meta":389,"navigation":44,"order":171,"path":390,"seo":391,"stage":392,"stem":393,"summary":394,"tools":395,"__hash__":408},"skills\u002Fskills\u002F01-data-modeling.md","Data & Modeling",{"type":8,"value":374,"toc":375},[],{"title":18,"searchDepth":19,"depth":19,"links":376},[],[378,380,383,387],{"name":379},"Dataset preparation and label sanity checks",{"name":381,"evidence":117,"evidenceHref":382},"Fine-tuning diffusion models (LoRA \u002F SDXL)","#project-genbatik",{"name":384,"evidence":385,"evidenceHref":386},"CNN \u002F embedding models for vision","SPARSE, IdentifEye","#project-sparse",{"name":388,"evidence":193,"evidenceHref":386},"Evaluation beyond a single accuracy number",{},"\u002Fskills\u002F01-data-modeling",{"title":372,"description":18},"01","skills\u002F01-data-modeling","Framing the problem, cleaning the data, training and evaluating a model I can defend.",[396,399,402,405],{"name":397,"icon":398},"PyTorch","simple-icons:pytorch",{"name":400,"icon":401},"Hugging Face","simple-icons:huggingface",{"name":403,"icon":404},"Python","simple-icons:python",{"name":406,"icon":407},"NumPy","simple-icons:numpy","BAxygUscJAcYvKaI3uHLM0HcTlsXMMyrMTX-fBBu71U",{"id":410,"title":411,"body":412,"description":18,"extension":22,"items":416,"meta":426,"navigation":44,"order":19,"path":427,"seo":428,"stage":429,"stem":430,"summary":431,"tools":432,"__hash__":445},"skills\u002Fskills\u002F02-serving.md","Serving & Inference",{"type":8,"value":413,"toc":414},[],{"title":18,"searchDepth":19,"depth":19,"links":415},[],[417,420,422,424],{"name":418,"evidence":419,"evidenceHref":382},"REST \u002F async job APIs around model inference","GenBatik, IdentifEye",{"name":421,"evidence":117,"evidenceHref":382},"Streaming progress to the client (WebSocket)",{"name":423,"evidence":193,"evidenceHref":386},"Containerised deploy, GPU vs CPU trade-offs",{"name":425,"evidence":117,"evidenceHref":382},"Latency and cost per request",{},"\u002Fskills\u002F02-serving",{"title":411,"description":18},"02","skills\u002F02-serving","A model that only runs in a notebook is not a feature. I put it behind an endpoint and keep it honest.",[433,436,439,442],{"name":434,"icon":435},"FastAPI","simple-icons:fastapi",{"name":437,"icon":438},"Go","simple-icons:go",{"name":440,"icon":441},"Docker","simple-icons:docker",{"name":443,"icon":444},"Cloudflare","simple-icons:cloudflare","XPBmW54RL6bUYVU8N1ePKHH0_xPXjuXhfZlvWHG4FuU",{"id":447,"title":448,"body":449,"description":18,"extension":22,"items":453,"meta":463,"navigation":44,"order":296,"path":464,"seo":465,"stage":466,"stem":467,"summary":468,"tools":469,"__hash__":479},"skills\u002Fskills\u002F03-monitoring.md","Monitoring & Iteration",{"type":8,"value":450,"toc":451},[],{"title":18,"searchDepth":19,"depth":19,"links":452},[],[454,456,459,461],{"name":455,"evidence":117,"evidenceHref":382},"Logging inputs\u002Foutputs to make failures reproducible",{"name":457,"evidence":313,"evidenceHref":458},"Feedback capture (accepted vs rejected output)","#project-tatakata",{"name":460},"Cost and usage tracking per feature",{"name":462},"Versioned experiments instead of one-off notebooks",{},"\u002Fskills\u002F03-monitoring",{"title":448,"description":18},"03","skills\u002F03-monitoring","The part most portfolios skip. I care about what happens after deploy: what broke, what drifted, and what the next version should change.",[470,473,476],{"name":471,"icon":472},"Postgres","simple-icons:postgresql",{"name":474,"icon":475},"Grafana","simple-icons:grafana",{"name":477,"icon":478},"GitHub Actions","simple-icons:githubactions","BuvCLD0gbuMJnM3Kdf6AZE4jzYjGaUkBJSDYLWlVHP0",{"id":481,"title":482,"body":483,"description":18,"extension":22,"items":487,"meta":499,"navigation":44,"order":358,"path":500,"seo":501,"stage":502,"stem":503,"summary":504,"tools":505,"__hash__":516},"skills\u002Fskills\u002F04-product.md","Product Delivery",{"type":8,"value":484,"toc":485},[],{"title":18,"searchDepth":19,"depth":19,"links":486},[],[488,491,493,496],{"name":489,"evidence":490,"evidenceHref":382},"Nuxt \u002F Vue front ends for data-heavy products","GenBatik, invasikode.com",{"name":492},"API design and schema work in Postgres",{"name":494,"evidence":495},"Static-first builds, prerender, and deploy pipelines","Portfolio (this site)",{"name":497,"evidence":498,"evidenceHref":37},"Technical writing that explains a system to non-authors","invasikode.com",{},"\u002Fskills\u002F04-product",{"title":482,"description":18},"04","skills\u002F04-product","An AI feature is only as good as the interface around it. I build that part too.",[506,509,512,515],{"name":507,"icon":508},"Nuxt","simple-icons:nuxt",{"name":510,"icon":511},"TypeScript","simple-icons:typescript",{"name":513,"icon":514},"Tailwind","simple-icons:tailwindcss",{"name":471,"icon":472},"Njr-puO0hUItdVfxSuLHRHWt8RMtCngHhvZMFGeBOuE",[518,533,547,561],{"id":519,"title":520,"body":521,"date":59,"description":525,"extension":22,"featured":44,"href":526,"meta":527,"navigation":44,"order":171,"outlet":528,"path":529,"seo":530,"stem":531,"__hash__":532},"writing\u002Fwriting\u002Freact-vue-angular.md","React, Vue, Angular, what to Decide?",{"type":8,"value":522,"toc":523},[],{"title":18,"searchDepth":19,"depth":19,"links":524},[],"Membandingkan Vue, Angular, dan React dari sisi trade-off nyata, bukan dari preferensi.","https:\u002F\u002Fwww.invasikode.com\u002Freact-angular-vue-pilih-framework-yang-mana",{},"Invasikode","\u002Fwriting\u002Freact-vue-angular",{"title":520,"description":525},"writing\u002Freact-vue-angular","6AWI_2LfssZQn6HxknUUrO0FI5v0FbbHbVY0bg9LdjY",{"id":534,"title":535,"body":536,"date":59,"description":540,"extension":22,"featured":44,"href":541,"meta":542,"navigation":44,"order":19,"outlet":528,"path":543,"seo":544,"stem":545,"__hash__":546},"writing\u002Fwriting\u002Fstate-of-js-2022.md","What's Up Javascript? SoJS 2022",{"type":8,"value":537,"toc":538},[],{"title":18,"searchDepth":19,"depth":19,"links":539},[],"Rangkuman hasil survei State of JS 2022 dari 40k responden dan apa artinya untuk pemilihan tooling.","https:\u002F\u002Fwww.invasikode.com\u002Fapa-kabar-javascript-rilis-state-of-js-2022",{},"\u002Fwriting\u002Fstate-of-js-2022",{"title":535,"description":540},"writing\u002Fstate-of-js-2022","ihWFJQpoenYUub-BubONr1T7YDa3_4V9lHFgUnMs_9Q",{"id":548,"title":549,"body":550,"date":59,"description":554,"extension":22,"featured":103,"href":555,"meta":556,"navigation":44,"order":296,"outlet":528,"path":557,"seo":558,"stem":559,"__hash__":560},"writing\u002Fwriting\u002Fnode-express-ts.md","Guide: Using Node and Express with TypeScript",{"type":8,"value":551,"toc":552},[],{"title":18,"searchDepth":19,"depth":19,"links":553},[],"Setup Node + Express dengan TypeScript dari nol, termasuk konfigurasi build dan type checking.","https:\u002F\u002Fwww.invasikode.com\u002Fmenggunakan-typescript-dengan-node-js-dan-express",{},"\u002Fwriting\u002Fnode-express-ts",{"title":549,"description":554},"writing\u002Fnode-express-ts","6_CMgFfmLxWgSzutFUNohal6Yl6c-VFxDEE5jeXClwE",{"id":562,"title":563,"body":564,"date":59,"description":568,"extension":22,"featured":103,"href":569,"meta":570,"navigation":44,"order":358,"outlet":528,"path":571,"seo":572,"stem":573,"__hash__":574},"writing\u002Fwriting\u002Fstarship.md","Make CMD-ing Fun Again with Starship",{"type":8,"value":565,"toc":566},[],{"title":18,"searchDepth":19,"depth":19,"links":567},[],"Memasang Starship prompt untuk PowerShell dan merapikan environment terminal sehari-hari.","https:\u002F\u002Fwww.invasikode.com\u002Fbuat-powershell-lebih-berwarna-dengan-starship",{},"\u002Fwriting\u002Fstarship",{"title":563,"description":568},"writing\u002Fstarship","jX0pDO7Jax5Ya6-Xy_3-vIO3c2lg6v543xC90l6HsXM",[],1792431174229]